diff --git a/features/banner.feature b/features/banner.feature new file mode 100644 index 000000000..6d2f59bbb --- /dev/null +++ b/features/banner.feature @@ -0,0 +1,36 @@ +@banner +@parallel +Feature: Banner + + Banners are used by Screwdriver admins to notify users about important updates or changes. + This feature ensures that all users are aware of critical information, such as system maintenance, new feature releases, or any other relevant announcements. + By using banners, admins can effectively communicate with users and ensure that they are informed in a timely manner. + + Rules: + - Only users with Screwdriver admin permissions can create banners. + - Banners can be scoped as Global, Pipeline, or Build. + - Once a banner is created, its scope cannot be changed. + - When a banner is created with Pipeline or Build scope, a scopeId is required. + - The banner message can be updated. + - Banners can be deleted. + + Background: + Given "calvin" is logged in + + Scenario: Banner with global scope + When they create new banner with message "Hello World" and "GLOBAL" scope + Then they can see that the banner is created with "GLOBAL" scope + And banner is "updated" when they update the banner with "message" "Some Random Message" + And banner is "not updated" when they update the banner with "scopeId" "1234" + And banner is "not updated" when they update the banner with "scope" "PIPELINE" + Then banner is deleted + + Scenario: Banner with pipeline scope + Given an existing pipeline + And there is no banner associated to that pipeline + When they create new banner with message "Hello World" and "PIPELINE" scope + Then they can see that the banner is created with "PIPELINE" scope + And they can get the banner associated to that pipeline + And banner is "updated" when they update the banner with "isActive" "false" + And banner is "not updated" when they update the banner with "scope" "GLOBAL" + Then banner is deleted diff --git a/features/step_definitions/banner.js b/features/step_definitions/banner.js new file mode 100644 index 000000000..9e833bc18 --- /dev/null +++ b/features/step_definitions/banner.js @@ -0,0 +1,150 @@ +'use strict'; + +const Assert = require('chai').assert; +const { Before, Then, When } = require('@cucumber/cucumber'); +const request = require('screwdriver-request'); +const { disableRunScenarioInParallel } = require('../support/parallel'); + +const TIMEOUT = 240 * 1000; + +disableRunScenarioInParallel(); + +Before('@banner', function hook() { + this.jwt = null; + this.bannerId = null; + + this.repoOrg = this.testOrg; + this.repoName = 'functional-git'; + this.pipelineId = null; +}); + +When( + /^they create new banner with message "([^"]*)" and "(GLOBAL|PIPELINE)" scope$/, + { timeout: TIMEOUT }, + function step(message, scope) { + const payload = { + message, + isActive: true, + type: 'info' + }; + + if (scope === 'PIPELINE') { + payload.scope = scope; + payload.scopeId = this.pipelineId; + } + + return request({ + url: `${this.instance}/${this.namespace}/banners`, + method: 'POST', + json: payload, + context: { + token: this.jwt + } + }).then(resp => { + Assert.equal(resp.statusCode, 201); + Assert.equal(resp.body.message, message); + Assert.equal(resp.body.isActive, true); + Assert.equal(resp.body.type, 'info'); + Assert.isNotNull(resp.body.id); + this.bannerId = resp.body.id; + }); + } +); + +Then( + /^they can see that the banner is created with "(GLOBAL|PIPELINE)" scope$/, + { timeout: TIMEOUT }, + function step(scope) { + return request({ + url: `${this.instance}/${this.namespace}/banners/${this.bannerId}`, + method: 'GET', + context: { + token: this.jwt + } + }).then(resp => { + Assert.equal(resp.statusCode, 200); + if (scope === 'PIPELINE') { + Assert.equal(resp.body.scope, scope.toUpperCase()); + Assert.equal(resp.body.scopeId, this.pipelineId); + + return; + } + Assert.equal(resp.body.scope, 'GLOBAL'); + }); + } +); + +Then( + /^banner is "(updated|not updated)" when they update the banner with "(message|scopeId|isActive|scope)" "([^"]*)"$/, + { timeout: TIMEOUT }, + function step(status, payloadType, payloadValue) { + const payload = {}; + + payload[payloadType] = payloadValue; + + return request({ + url: `${this.instance}/${this.namespace}/banners/${this.bannerId}`, + method: 'PUT', + json: payload, + context: { + token: this.jwt + } + }) + .then(resp => { + if (status === 'updated') { + Assert.equal(resp.statusCode, 200); + Assert.equal(resp.body[payloadType].toString(), payloadValue); + } else { + throw new Error('Banner should not be updated'); + } + }) + .catch(err => { + if (status === 'not updated') { + Assert.equal(err.statusCode, 400); + Assert.include(err.message, 'Invalid request payload input'); + } else { + throw err; + } + }); + } +); + +Then(/^banner is deleted$/, { timeout: TIMEOUT }, function step() { + return request({ + url: `${this.instance}/${this.namespace}/banners/${this.bannerId}`, + method: 'DELETE', + context: { + token: this.jwt + } + }).then(resp => { + Assert.equal(resp.statusCode, 204); + this.bannerId = null; + }); +}); + +Then(/^there is no banner associated to that pipeline$/, { timeout: TIMEOUT }, function step() { + return request({ + url: `${this.instance}/${this.namespace}/banners?scopeId=${this.pipelineId}`, + method: 'GET', + context: { + token: this.jwt + } + }).then(resp => { + Assert.equal(resp.statusCode, 200); + Assert.equal(resp.body.length, 0); + }); +}); + +Then(/^they can get the banner associated to that pipeline$/, { timeout: TIMEOUT }, function step() { + return request({ + url: `${this.instance}/${this.namespace}/banners?scopeId=${this.pipelineId}&scope=PIPELINE`, + method: 'GET', + context: { + token: this.jwt + } + }).then(resp => { + Assert.equal(resp.statusCode, 200); + Assert.equal(resp.body.length, 1); + Assert.equal(resp.body[0].scopeId, this.pipelineId); + }); +});