diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 631457a3..07573ce8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -34,8 +34,8 @@ jobs: - name: Install playwright browsers run: yarn playwright install chromium - - name: Run Playwright Chrome Tests - run: yarn test:playwright-local:ci:chromium + - name: Run Cucumber Giftaid Sanity Tests + run: yarn test:cucumber:ci:chromium:sanity continue-on-error: false env: NODE_ENV: development @@ -85,8 +85,8 @@ jobs: - name: Install playwright browsers (WebKit) run: yarn playwright install webkit - - name: Run Mobile WebKit Tests - run: yarn test:playwright-local:ci:mobile + - name: Run Mobile Cucumber Tests + run: yarn test:cucumber:ci:mobile:sanity continue-on-error: false env: NODE_ENV: development diff --git a/package.json b/package.json index ce0981d7..0288ea35 100644 --- a/package.json +++ b/package.json @@ -37,15 +37,20 @@ "start": "node scripts/start.js", "build": "node scripts/build.js", "lint": "eslint --color src", - "test:playwright-local:local": "playwright test --config=./playwright-local/playwright-local.config.js", - "test:playwright-local:local:chromium": "playwright test --config=./playwright-local/playwright-local.config.js --project=chromium", - "test:playwright-local:local:chromium--h": "playwright test --config=./playwright-local/playwright-local.config.js --project=chromium --headed", - "test:playwright-local:local:mobile": "playwright test --config=./playwright-local/playwright-local.config.js --project=mobile-safari", - "test:playwright-local:local:mobile--h": "playwright test --config=./playwright-local/playwright-local.config.js --project=mobile-safari --headed", - "test:playwright-local:ci": "export NODE_ENV=development; start-server-and-test start http://localhost:3000 test:playwright-local", - "test:playwright-local:ci:chromium": "export NODE_ENV=development; start-server-and-test start http://localhost:3000 test:playwright-local:local:chromium", - "test:playwright-local:ci:mobile": "export NODE_ENV=development; start-server-and-test start http://localhost:3000 test:playwright-local:local:mobile", - "test:playwright-local:local:chrome:mobile": "export NODE_ENV=development; start-server-and-test start http://localhost:3000 test:playwright-local:local", + "test:cucumber:local": "cd playwright-local && BASE_URL=http://localhost:3000 cucumber-js --config config/cucumber.js", + "test:cucumber:local:sanity": "cd playwright-local && BASE_URL=http://localhost:3000 cucumber-js --config config/cucumber.js --tags '@sanity'", + "test:cucumber:local:chromium": "cd playwright-local && BROWSER=chromium BASE_URL=http://localhost:3000 cucumber-js --config config/cucumber.js", + "test:cucumber:local:chromium:sanity": "cd playwright-local && BROWSER=chromium BASE_URL=http://localhost:3000 cucumber-js --config config/cucumber.js --tags '@sanity'", + "test:cucumber:local:chromium:headed": "cd playwright-local && BROWSER=chromium HEADED=true BASE_URL=http://localhost:3000 cucumber-js --config config/cucumber.js --tags '@sanity'", + "test:cucumber:local:mobile": "cd playwright-local && BROWSER=mobile-safari BASE_URL=http://localhost:3000 cucumber-js --config config/cucumber.js", + "test:cucumber:local:mobile:sanity": "cd playwright-local && BROWSER=mobile-safari BASE_URL=http://localhost:3000 cucumber-js --config config/cucumber.js --tags '@sanity'", + "test:cucumber:local:mobile:headed": "cd playwright-local && BROWSER=mobile-safari HEADED=true BASE_URL=http://localhost:3000 cucumber-js --config config/cucumber.js --tags '@sanity'", + "test:cucumber:ci": "export NODE_ENV=development; start-server-and-test start http://localhost:3000 test:cucumber:local", + "test:cucumber:ci:sanity": "export NODE_ENV=development; start-server-and-test start http://localhost:3000 test:cucumber:local:sanity", + "test:cucumber:ci:chromium": "export NODE_ENV=development; start-server-and-test start http://localhost:3000 test:cucumber:local:chromium", + "test:cucumber:ci:chromium:sanity": "export NODE_ENV=development; start-server-and-test start http://localhost:3000 test:cucumber:local:chromium:sanity", + "test:cucumber:ci:mobile": "export NODE_ENV=development; start-server-and-test start http://localhost:3000 test:cucumber:local:mobile", + "test:cucumber:ci:mobile:sanity": "export NODE_ENV=development; start-server-and-test start http://localhost:3000 test:cucumber:local:mobile:sanity", "snyk-protect": "snyk-protect", "prepublish": "yarn snyk-protect" }, @@ -59,6 +64,7 @@ "@babel/runtime": "^7.11.2", "@comicrelief/data-models": "^1.15.4", "@comicrelief/test-utils": "^1.5.13", + "@cucumber/cucumber": "8.9.1", "@playwright/test": "1.38.1", "babel-eslint": "^10.1.0", "babel-loader": "^8.1.0", diff --git a/playwright-local/config/cucumber.js b/playwright-local/config/cucumber.js new file mode 100644 index 00000000..680c684d --- /dev/null +++ b/playwright-local/config/cucumber.js @@ -0,0 +1,15 @@ +module.exports = { + default: { + paths: ['tests/features/**/*.feature'], + require: [ + 'tests/support/**/*.js', + 'tests/step-definitions/**/*.js', + ], + // Use 'pretty' locally for readable step-by-step output. + // On CI keep the output minimal to avoid noisy logs. + format: ['progress', 'summary'], + retry: 2, // Retry failed scenarios twice + parallel: 2, // Run scenarios in parallel workers + publishQuiet: true, + }, +}; diff --git a/playwright-local/playwright-local.config.js b/playwright-local/playwright-local.config.js deleted file mode 100644 index ad93f15e..00000000 --- a/playwright-local/playwright-local.config.js +++ /dev/null @@ -1,60 +0,0 @@ -// @ts-check -const { defineConfig, devices } = require('@playwright/test'); - -/** - * Read environment variables from file. - * https://github.com/motdotla/dotenv - */ -require('dotenv').config(); - -/** - * @see https://playwright.dev/docs/test-configuration - */ -module.exports = defineConfig({ - testDir: '.', - /* Fail the build on CI if you accidentally left test.only in the source code. */ - forbidOnly: !!process.env.CI, - retries: 1, - workers: 2, - timeout: 60 * 1000, - expect: { - timeout: 60 * 1000, - }, - reporter: [ - ['list'], - ['html', { open: 'never' }] - ], - use: { - actionTimeout: 0, - /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ - trace: 'on-first-retry', - }, - - /* Configure projects for major browsers */ - projects: [ - { - name: 'chromium', - use: { - ...devices['Desktop Chrome'], - viewport: { - width: 1300, - height: 1000 - } - } - }, - { - name: 'mobile-safari', - use: { ...devices['iPhone 12'] }, - timeout: 360_000, - expect: { timeout: 20_000 } - } - ], - - /* Run your local dev server before starting the tests */ - webServer: { - command: 'yarn start', - port: 3000, - timeout: 120000, - reuseExistingServer: true, - }, -}); diff --git a/playwright-local/tests/features/submit/addressValidation.feature b/playwright-local/tests/features/submit/addressValidation.feature new file mode 100644 index 00000000..de9608f8 --- /dev/null +++ b/playwright-local/tests/features/submit/addressValidation.feature @@ -0,0 +1,43 @@ +@sanity @address-validation +Feature: Address validation + + Background: + Given I am on the local Giftaid page + And I select the local Giftaid option + And I enter the local supporter details + + Scenario: Empty postcode should show an error message + When I clear the local postcode field + And I submit the local Giftaid form + Then I should see the local postcode error message "Please enter your postcode" + + Scenario Outline: Invalid postcodes should show error messages + When I enter the local postcode "" + Then I should see the local postcode error message "" + + Examples: + | postcode | message | + | 12SE17TP | Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below. | + | comic relief | Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below. | + | cro 7tp | Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below. | + + Scenario: Entering a postcode without selecting an address should show an error message + When I enter the local postcode "SE1 7TP" + And I search for the local postcode + Then I should see the local address dropdown + When I submit the local Giftaid form + Then I should see the local address select error message "Please select your address" + + Scenario: Clicking the manual address link should show the address fields + When I enter the local postcode "SE1 7TP" + Then I should see the local manual address link + When I click the local manual address link + Then I should see the local manual address fields + + Scenario: Invalid address fields should show error messages + When I enter the local postcode "SE1 7TP" + And I click the local manual address link + And I enter the local invalid address line 1 + Then I should see the local address line 1 error message + When I enter the local invalid town + Then I should see the local town error message diff --git a/playwright-local/tests/features/submit/formValidation.feature b/playwright-local/tests/features/submit/formValidation.feature new file mode 100644 index 00000000..e23f25d7 --- /dev/null +++ b/playwright-local/tests/features/submit/formValidation.feature @@ -0,0 +1,55 @@ +@sanity @form-validation +Feature: Form validation + + Background: + Given I am on the local Giftaid page + And I select the local Giftaid option + + Scenario Outline: Invalid mobile numbers should show an error message + When I enter the local mobile number "" + Then I should see the local mobile error message "Please enter a valid mobile phone number - it must be the same number associated with your donation." + + Examples: + | mobile | + | 0712345678 | + | 0712345678900 | + | 0712 345 6789 | + | 0780ab5694245 | + + Scenario: Valid mobile number should submit the form + When I complete the local Giftaid form with valid details + And I submit the local Giftaid form + Then I should see the local thank you message "Thank you, test!" + + Scenario Outline: Invalid first names should show an error message + When I enter the local first name "" + Then I should see the local first name error message "This field only accepts 25 alphabetic characters and ' - starting with alphabetic characters" + + Examples: + | firstName | + | Test^$%£ | + | SPACE | + | 123Test | + + Scenario: Valid first name should submit the form + When I complete the local Giftaid form with the first name "testFirstname" + And I submit the local Giftaid form + Then I should see the local thank you message "Thank you, testFirstname!" + + Scenario Outline: Invalid last names should show an error message + When I enter the local last name "" + Then I should see the local last name error message "This field only accepts 25 alphanumeric characters and , . ( ) / & ' - starting with alphanumeric characters" + + Examples: + | lastName | + | Test^$%£ | + | SPACE | + + Scenario: Alphanumeric last name should not show an error message + When I enter the local last name "123Test" + Then I should not see the local last name error message + + Scenario: Valid last name should submit the form + When I complete the local Giftaid form with valid details + And I submit the local Giftaid form + Then I should see the local thank you message containing "Thank you," diff --git a/playwright-local/tests/features/submit/internationalAddressesValidation.feature b/playwright-local/tests/features/submit/internationalAddressesValidation.feature new file mode 100644 index 00000000..1c17266d --- /dev/null +++ b/playwright-local/tests/features/submit/internationalAddressesValidation.feature @@ -0,0 +1,18 @@ +@sanity @international-address-validation +Feature: International address validation + + Background: + Given I am on the local Giftaid page + And I select the local Giftaid option + And I enter the local supporter details + + Scenario: Selecting a non-UK country and entering a non-UK postcode should submit the form + When I enter the local postcode "30916-395" + Then I should see the local postcode error message "Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below." + When I click the local manual address link + And I enter the local international address details + And I select a random local non-UK country + Then I should not see the local postcode error message + When I select the local marketing preferences + And I submit the local Giftaid form + Then I should see the local thank you message containing "Thank you," diff --git a/playwright-local/tests/features/submit/marketingPreferencesValidation.feature b/playwright-local/tests/features/submit/marketingPreferencesValidation.feature new file mode 100644 index 00000000..a1ab4e1a --- /dev/null +++ b/playwright-local/tests/features/submit/marketingPreferencesValidation.feature @@ -0,0 +1,36 @@ +@sanity @marketing-preferences-validation +Feature: Marketing preferences validation + + Background: + Given I am on the local Giftaid page + And I select the local Giftaid option + And I complete the local Giftaid form with valid details + + Scenario: Clicking marketing preference options should submit the Giftaid form + When I select all the local marketing preference options + And I enter the local marketing email + And I enter the local marketing phone + And I submit the local Giftaid form + Then I should see the local thank you message "Thank you, test!" + + Scenario: The email marketing preference field should show validation errors + When I select the local email marketing preference + And I enter the local marketing email + And I clear the local marketing email + Then I should see the local marketing email error message "Please fill in your email address" + When I enter an invalid local marketing email "example@£$^&email.com" + Then I should see the local marketing email error message "Please fill in a valid email address" + When I enter the local marketing email + And I submit the local Giftaid form + Then I should see the local thank you message "Thank you, test!" + + Scenario: The phone marketing preference field should show validation errors + When I select the local phone marketing preference + And I enter the local marketing phone + And I clear the local marketing phone + Then I should see the local marketing phone error message "Please fill in your phone number" + When I enter an invalid local marketing phone "0208569424" + Then I should see the local marketing phone error message "Please fill in a valid UK phone number, with no spaces" + When I enter the local marketing phone + And I submit the local Giftaid form + Then I should see the local thank you message containing "Thank you," diff --git a/playwright-local/tests/features/submit/postcodeLookup.feature b/playwright-local/tests/features/submit/postcodeLookup.feature new file mode 100644 index 00000000..12668ad8 --- /dev/null +++ b/playwright-local/tests/features/submit/postcodeLookup.feature @@ -0,0 +1,23 @@ +@sanity @postcode-lookup +Feature: Postcode lookup validation + + Background: + Given I am on the local Giftaid page + And I select the local Giftaid option + And I enter the local supporter details + + Scenario Outline: Postcode formatting errors should show validation message + When I enter the local postcode "" + Then I should see the local postcode error message "" + + Examples: + | postcode | message | + | S E 1 7 T P | Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below. | + | SE$%TP | Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below. | + + Scenario: Enter valid UK postcode using postcode lookup should submit the form + When I enter the local postcode "SE1 7TP" + And I search for the local postcode + And I select the local lookup address or enter the address manually + And I submit the local Giftaid form + Then I should see the local thank you message containing "Thank you," diff --git a/playwright-local/tests/features/submit/validGiftaidSubmission.feature b/playwright-local/tests/features/submit/validGiftaidSubmission.feature new file mode 100644 index 00000000..a9f30572 --- /dev/null +++ b/playwright-local/tests/features/submit/validGiftaidSubmission.feature @@ -0,0 +1,12 @@ +@sanity @valid-giftaid-submission +Feature: Valid Giftaid Submission + + Background: + Given I am on the local Giftaid page + And I select the local Giftaid option + + Scenario: Valid Giftaid Submission + When I complete the local Giftaid form with valid details + And I select the local marketing preferences + And I submit the local Giftaid form and wait for the navigation + Then I should see the local thank you message containing "Thank you," diff --git a/playwright-local/tests/features/update/updateFormValidation.feature b/playwright-local/tests/features/update/updateFormValidation.feature new file mode 100644 index 00000000..84f9c2bb --- /dev/null +++ b/playwright-local/tests/features/update/updateFormValidation.feature @@ -0,0 +1,104 @@ +@sanity @update-form-validation +Feature: Giftaid update form validation + + Background: + Given I am on the local Giftaid update page + + Scenario: Empty input fields should show error messages + When I submit the local Giftaid form + Then I should see the local update required field error messages + When I click the local manual address link + Then I should see the local update manual address required error messages + And I should see the local update GiftAid declaration error message + + Scenario Outline: Invalid update first names should show an error message + When I enter the local first name "" + Then I should see the local update first name error message "This field only accepts alphabetic characters and ' -" + + Examples: + | firstName | + | Test^$%£ | + | 123Test | + | SPACE | + + Scenario: A valid update first name should submit the form + When I complete the local Giftaid update form with valid details + And I select yes for the local update GiftAid declaration + And I submit the local Giftaid form + Then I should see the local thank you message containing "Thank you," + + Scenario Outline: Invalid update last names should show an error message + When I enter the local last name "" + Then I should see the local update last name error message "This field only accepts alphanumeric characters and , . ( ) / & ' -" + + Examples: + | lastName | + | Test^$%£ | + | SPACE | + + Scenario: An alphanumeric update last name should not show an error message + When I enter the local last name "123Test" + Then I should not see the local last name error message + + Scenario Outline: Invalid update emails should show an error message + When I enter the local email "" + Then I should see the local update email error message "Please fill in a valid email address" + + Examples: + | email | + | test@comic$relief.com | + | test@c{(micrelief.com | + | test@comic%relief.com | + | Test0-9!#$%&'*+/=?^_{\|}~-@comicrelief_9-8.com.uk | + + Scenario: A valid special character email should not show an error message + When I enter the local email "te$%^st@comicrelief.com" + Then I should not see the local update email error message + + Scenario Outline: Update mobile number validation + When I enter the local mobile number "" + Then I should see the local mobile error message "" + + Examples: + | mobile | message | + | 0722345678 | Please enter a valid mobile phone number - it must be the same number associated with your donation. | + | 0722345678900 | Please enter a valid mobile phone number - it must be the same number associated with your donation. | + | 0722 345 6789 | Please enter a valid mobile phone number - it must be the same number associated with your donation. | + | 0780ab5694245 | Please enter a valid mobile phone number - it must be the same number associated with your donation. | + + Scenario Outline: Valid update mobile numbers should not show an error message + When I enter the local mobile number "" + Then I should not see the local mobile error message + + Examples: + | mobile | + | 07123456789 | + | 07340707252 | + + Scenario Outline: Invalid update postcodes should show an error message + When I enter the local postcode "" + Then I should see the local postcode error message "Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below." + + Examples: + | postcode | + | S E 1 7 T P | + | SE$%TP | + | cro 7tp | + + Scenario Outline: Compact UK postcodes should not show a format error + When I enter the local postcode "" + Then I should not see the local postcode error message + + Examples: + | postcode | + | se17tp | + | SE17TP | + + Scenario: Entering a valid UK postcode on the update form using postcode lookup should submit the form + When I complete the local update supporter details + And I enter the local postcode "SE1 7TP" + And I search for the local postcode + And I select the local update lookup address or enter the address manually + And I select yes for the local update GiftAid declaration + And I submit the local Giftaid form + Then I should see the local thank you message containing "Thank you," diff --git a/playwright-local/tests/features/update/updateInternationalAddressesValidation.feature b/playwright-local/tests/features/update/updateInternationalAddressesValidation.feature new file mode 100644 index 00000000..634d4ae9 --- /dev/null +++ b/playwright-local/tests/features/update/updateInternationalAddressesValidation.feature @@ -0,0 +1,17 @@ +@sanity @update-international-address-validation +Feature: International address validation on the update form + + Background: + Given I am on the local Giftaid update page + + Scenario: Selecting a non-UK country and entering a non-UK postcode should submit the update form + When I complete the local update supporter details + And I enter the local postcode "30916-395" + Then I should see the local postcode error message "Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below." + When I click the local manual address link + And I enter the local update international address details + And I select a random local update non-UK country + Then I should not see the local postcode error message + When I select yes for the local update GiftAid declaration + And I submit the local Giftaid form + Then I should see the local thank you message containing "Thank you," diff --git a/playwright-local/tests/features/update/updateValidFormSubmission.feature b/playwright-local/tests/features/update/updateValidFormSubmission.feature new file mode 100644 index 00000000..8f490608 --- /dev/null +++ b/playwright-local/tests/features/update/updateValidFormSubmission.feature @@ -0,0 +1,11 @@ +@sanity @update-valid-form-submission +Feature: Valid Giftaid update submission + + Background: + Given I am on the local Giftaid update page + + Scenario: Valid Giftaid update submission + When I complete the local Giftaid update form with valid details + And I select yes for the local update GiftAid declaration + And I submit the local Giftaid form + Then I should see the local thank you message containing "Thank you," diff --git a/playwright-local/tests/step-definitions/common/giftaidCommon.steps.js b/playwright-local/tests/step-definitions/common/giftaidCommon.steps.js new file mode 100644 index 00000000..44fbf2d2 --- /dev/null +++ b/playwright-local/tests/step-definitions/common/giftaidCommon.steps.js @@ -0,0 +1,60 @@ +const { Given, When, Then } = require('@cucumber/cucumber'); +const { expect } = require('@playwright/test'); + +Given('I am on the local Giftaid page', async function () { + await this.page.goto(process.env.BASE_URL, { + waitUntil: 'networkidle', + timeout: 60000, + }); +}); + +Given('I select the local Giftaid option', async function () { + await this.page.locator('#field-label--giftaid').click(); +}); + +Given('I enter the local supporter details', async function () { + await this.page.locator('#field-input--mobile').fill('07123456789'); + await this.page.locator('input#field-input--firstname').fill('test'); + await this.page.locator('input#field-input--lastname').fill('user'); +}); + +When('I submit the local Giftaid form', async function () { + await this.page.locator('button[type=submit]').click(); +}); + +When('I enter the local postcode {string}', async function (postcode) { + await this.page.locator('input#field-input--postcode').fill(postcode); +}); + +When('I search for the local postcode', async function () { + await this.page.locator('#postcode_button').click(); +}); + +When('I click the local manual address link', async function () { + await this.page.locator('a[aria-describedby=field-error--addressDetails]').click(); +}); + +Then('I should see the local postcode error message {string}', async function (message) { + await expect(this.page.locator('div#field-error--postcode > span')).toBeVisible(); + await expect(this.page.locator('div#field-error--postcode > span')).toContainText(message); +}); + +Then('I should not see the local postcode error message', async function () { + await expect(this.page.locator('div#field-error--postcode > span')).not.toBeVisible(); +}); + +Then('I should see the local thank you message {string}', async function (message) { + await expect(this.page.locator('div > h1')).toHaveText(message, { timeout: 15000 }); +}); + +Then('I should see the local thank you message containing {string}', async function (message) { + await expect(this.page.locator('div > h1')).toContainText(message, { timeout: 15000 }); +}); + +When('I complete the local Giftaid form with valid details', async function () { + await this.commands.populateFormFields(this.page, { mobile: '07123456789' }); +}); + +When('I select the local marketing preferences', async function () { + await this.commands.selectMarketingPrefs(this.page); +}); diff --git a/playwright-local/tests/step-definitions/submit/addressValidation.steps.js b/playwright-local/tests/step-definitions/submit/addressValidation.steps.js new file mode 100644 index 00000000..59a2272e --- /dev/null +++ b/playwright-local/tests/step-definitions/submit/addressValidation.steps.js @@ -0,0 +1,47 @@ +const { Given, When, Then } = require('@cucumber/cucumber'); +const { expect } = require('@playwright/test'); + +When('I clear the local postcode field', async function () { + await this.page.locator('input#field-input--postcode').fill(''); +}); + +Then('I should see the local address dropdown', async function () { + await expect(this.page.locator('#field-select--addressSelect')).toBeVisible(); +}); + +Then('I should see the local address select error message {string}', async function (message) { + await expect(this.page.locator('div#field-error--addressSelect > span')).toHaveText(message); +}); + +Then('I should see the local manual address link', async function () { + await expect(this.page.locator('a[aria-describedby=field-error--addressDetails]')).toBeVisible(); +}); + +Then('I should see the local manual address fields', async function () { + await expect(this.page.locator('#field-input--address1')).toBeVisible(); + await expect(this.page.locator('#field-input--address2')).toBeVisible(); + await expect(this.page.locator('#field-input--address3')).toBeVisible(); + await expect(this.page.locator('#field-input--town')).toBeVisible(); + await expect(this.page.locator('select#field-select--country')).toBeVisible(); +}); + +When('I enter the local invalid address line 1', async function () { + // Should see error message for address1 when input with special characters is entered + await this.page.locator('#field-input--address1').fill('@£%3dComic Relief'); +}); + +Then('I should see the local address line 1 error message', async function () { + await expect(this.page.locator('#field-error--address1 > span')).toHaveText( + "This field only accepts alphanumeric characters and ' . - & _ /" + ); +}); + +When('I enter the local invalid town', async function () { + await this.page.locator('#field-input--town').fill(' Comic Relief'); +}); + +Then('I should see the local town error message', async function () { + await expect(this.page.locator('#field-error--town > span')).toHaveText( + "This field only accepts alphanumeric characters and ' . - & _ /" + ); +}); diff --git a/playwright-local/tests/step-definitions/submit/formValidation.steps.js b/playwright-local/tests/step-definitions/submit/formValidation.steps.js new file mode 100644 index 00000000..437a965f --- /dev/null +++ b/playwright-local/tests/step-definitions/submit/formValidation.steps.js @@ -0,0 +1,37 @@ +const { When, Then } = require('@cucumber/cucumber'); +const { expect } = require('@playwright/test'); + +When('I enter the local mobile number {string}', async function (mobile) { + await this.page.locator('#field-input--mobile').fill(''); + await this.page.locator('#field-input--mobile').type(mobile, { delay: 100 }); +}); + +Then('I should see the local mobile error message {string}', async function (message) { + await expect(this.page.locator('div#field-error--mobile > span')).toHaveText(message); +}); + +When('I complete the local Giftaid form with the first name {string}', async function (firstName) { + await this.commands.populateFormFields(this.page, { firstName }); +}); + +When('I enter the local first name {string}', async function (firstName) { + const value = firstName === 'SPACE' ? ' ' : firstName; + await this.page.locator('#field-input--firstname').fill(value); +}); + +Then('I should see the local first name error message {string}', async function (message) { + await expect(this.page.locator('div#field-error--firstname')).toHaveText(message); +}); + +When('I enter the local last name {string}', async function (lastName) { + const value = lastName === 'SPACE' ? ' ' : lastName; + await this.page.locator('#field-input--lastname').fill(value); +}); + +Then('I should see the local last name error message {string}', async function (message) { + await expect(this.page.locator('div#field-error--lastname > span')).toHaveText(message); +}); + +Then('I should not see the local last name error message', async function () { + await expect(this.page.locator('div#field-error--lastname > span')).toHaveCount(0); +}); diff --git a/playwright-local/tests/step-definitions/submit/internationalAddressesValidation.steps.js b/playwright-local/tests/step-definitions/submit/internationalAddressesValidation.steps.js new file mode 100644 index 00000000..918f5ab0 --- /dev/null +++ b/playwright-local/tests/step-definitions/submit/internationalAddressesValidation.steps.js @@ -0,0 +1,20 @@ +const { When, Then } = require('@cucumber/cucumber'); +const { expect } = require('@playwright/test'); + +When('I enter the local international address details', async function () { + await this.page.locator('#field-input--address1').fill('219 Beacon St'); + await this.page.locator('#field-input--address2').fill('Winder'); + await this.page.locator('input#field-input--town').fill('GA'); +}); + +When('I select a random local non-UK country', async function () { + const countries = await this.page.$$eval('select#field-select--country > option', options => + options + .map(option => option.value) + .filter(value => value && value !== 'GB') + ); + + const randomCountryCode = countries[Math.floor(Math.random() * countries.length)]; + + await this.page.selectOption('select[name="country"]', { value: randomCountryCode }); +}); diff --git a/playwright-local/tests/step-definitions/submit/marketingPreferencesValidation.steps.js b/playwright-local/tests/step-definitions/submit/marketingPreferencesValidation.steps.js new file mode 100644 index 00000000..4fb5adf9 --- /dev/null +++ b/playwright-local/tests/step-definitions/submit/marketingPreferencesValidation.steps.js @@ -0,0 +1,67 @@ +const { When, Then } = require('@cucumber/cucumber'); +const { expect } = require('@playwright/test'); + +const Chance = require('chance'); +const chance = new Chance(); + +const email = `giftaid-staging-${Date.now().toString()}@email.sls.comicrelief.com`; +const phone = chance.phone({ country: 'uk', mobile: true }).replace(/\s/g, ''); + +When('I select all the local marketing preference options', async function () { + const marketingOptions = [ + '[aria-label="field-label--Email--Email"]', + '[aria-label="field-label--Phone--Phone"]', + '[aria-label="field-label--Text--SMS"]', + ]; + + for (const option of marketingOptions) { + const checkbox = this.page.locator(option); + + await checkbox.scrollIntoViewIfNeeded(); + await checkbox.check({ force: true }); + + await expect(checkbox).toBeChecked(); + } +}); + +When('I select the local email marketing preference', async function () { + await this.page.locator('[aria-label="field-label--Email--Email"]').click(); +}); + +When('I select the local phone marketing preference', async function () { + await this.page.locator('[aria-label="field-label--Phone--Phone"]').click(); +}); + +When('I enter the local marketing email', async function () { + await expect(this.page.locator('input#field-input--email')).toBeVisible(); + await this.page.locator('input#field-input--email').fill(email); +}); + +When('I clear the local marketing email', async function () { + await this.page.locator('input#field-input--email').fill(''); +}); + +When('I enter an invalid local marketing email {string}', async function (invalidEmail) { + await this.page.locator('input#field-input--email').fill(invalidEmail); +}); + +Then('I should see the local marketing email error message {string}', async function (message) { + await expect(this.page.locator('#field-error--email')).toHaveText(message); +}); + +When('I enter the local marketing phone', async function () { + await expect(this.page.locator('input#field-input--phone')).toBeVisible(); + await this.page.locator('input#field-input--phone').fill(phone); +}); + +When('I clear the local marketing phone', async function () { + await this.page.locator('input#field-input--phone').fill(''); +}); + +When('I enter an invalid local marketing phone {string}', async function (invalidPhone) { + await this.page.locator('input#field-input--phone').fill(invalidPhone); +}); + +Then('I should see the local marketing phone error message {string}', async function (message) { + await expect(this.page.locator('div#field-error--phone > span')).toHaveText(message); +}); diff --git a/playwright-local/tests/step-definitions/submit/postcodeLookup.steps.js b/playwright-local/tests/step-definitions/submit/postcodeLookup.steps.js new file mode 100644 index 00000000..97571800 --- /dev/null +++ b/playwright-local/tests/step-definitions/submit/postcodeLookup.steps.js @@ -0,0 +1,18 @@ +const { When } = require('@cucumber/cucumber'); +const { expect } = require('@playwright/test'); + +When('I select the local lookup address or enter the address manually', async function () { + if (await this.page.locator('#field-select--addressSelect').isVisible()) { + await this.page.locator('select#field-select--addressSelect').selectOption({ + label: 'COMIC RELIEF, CAMELFORD HOUSE 87-90', + }); + + await expect(this.page.locator('input#field-input--postcode')).toHaveValue('SE1 7TP'); + } else { + await this.page.locator('a[aria-describedby=field-error--addressDetails]').click(); + await this.page.locator('#field-input--address1').fill('COMIC RELIEF'); + await this.page.locator('#field-input--address2').fill('CAMELFORD HOUSE 87-90'); + await this.page.locator('#field-input--address3').fill('ALBERT EMBANKMENT'); + await this.page.locator('#field-input--town').fill('LONDON'); + } +}); diff --git a/playwright-local/tests/step-definitions/submit/validGiftaidSubmission.steps.js b/playwright-local/tests/step-definitions/submit/validGiftaidSubmission.steps.js new file mode 100644 index 00000000..8acb0dd8 --- /dev/null +++ b/playwright-local/tests/step-definitions/submit/validGiftaidSubmission.steps.js @@ -0,0 +1,7 @@ +const { When } = require('@cucumber/cucumber'); +const { expect } = require('@playwright/test'); + +When('I submit the local Giftaid form and wait for the navigation', async function () { + await this.page.locator('button[type=submit]').click(); + await expect(this.page.locator('div > h1')).toContainText(/Thank you,|Sorry!/); +}); diff --git a/playwright-local/tests/step-definitions/update/giftaidUpdateCommon.steps.js b/playwright-local/tests/step-definitions/update/giftaidUpdateCommon.steps.js new file mode 100644 index 00000000..097624c6 --- /dev/null +++ b/playwright-local/tests/step-definitions/update/giftaidUpdateCommon.steps.js @@ -0,0 +1,22 @@ +const { Given, When } = require('@cucumber/cucumber'); + +Given('I am on the local Giftaid update page', async function () { + await this.page.goto(`${process.env.BASE_URL}/update`, { + waitUntil: 'networkidle', + timeout: 60000, + }); +}); + +When('I complete the local Giftaid update form with valid details', async function () { + await this.commands.populateUpdateFormFields(this.page); +}); + +When('I complete the local update supporter details', async function () { + await this.page.locator('#field-input--firstname').fill('test'); + await this.page.locator('#field-input--lastname').fill('test lastname'); + await this.page.locator('input#field-input--email').fill('giftaid-staging-@email.sls.comicrelief.com'); +}); + +When('I select yes for the local update GiftAid declaration', async function () { + await this.page.locator('#giftAidClaimChoice>div:nth-child(2)>label').click(); +}); diff --git a/playwright-local/tests/step-definitions/update/updateFormValidation.steps.js b/playwright-local/tests/step-definitions/update/updateFormValidation.steps.js new file mode 100644 index 00000000..16fb6ae6 --- /dev/null +++ b/playwright-local/tests/step-definitions/update/updateFormValidation.steps.js @@ -0,0 +1,64 @@ +const { Given, When, Then } = require('@cucumber/cucumber'); +const { expect } = require('@playwright/test'); + +Then('I should see the local update required field error messages', async function () { + await expect(this.page.locator('div#field-error--firstname > span')).toContainText('Please fill in your first name'); + await expect(this.page.locator('div#field-error--lastname > span')).toContainText('Please fill in your last name'); + await expect(this.page.locator('div#field-error--postcode > span')).toContainText('Please enter your postcode'); + await expect(this.page.locator('div#field-error--addressDetails > span')).toContainText('Please fill in your address'); +}); + +Then('I should see the local update manual address required error messages', async function () { + await expect(this.page.locator('div#field-error--address1 > span')).toContainText('Please fill in your address line 1'); + await expect(this.page.locator('div#field-error--town > span')).toContainText('Please fill in your town/city'); +}); + +Then('I should see the local update GiftAid declaration error message', async function () { + await expect(this.page.locator('div#field-error--giftAidClaimChoice > span')).toContainText('This field is required'); +}); + +Then('I should see the local update first name error message {string}', async function (message) { + await expect(this.page.locator('#field-error--firstname')).toContainText(message); +}); + +Then('I should see the local update last name error message {string}', async function (message) { + await expect(this.page.locator('div#field-error--lastname > span')).toContainText(message); +}); + +When('I enter the local email {string}', async function (email) { + await this.page.locator('input#field-input--email').fill(email); +}); + +Then('I should see the local update email error message {string}', async function (message) { + await expect(this.page.locator('div#field-error--email > span')).toContainText(message); +}); + +Then('I should not see the local update email error message', async function () { + await expect(this.page.locator('div#field-error--email > span')).not.toBeVisible(); +}); + +Then('I should not see the local mobile error message', async function () { + await expect(this.page.locator('div#field-error--mobile > span')).not.toBeVisible(); +}); + +When('I select the local update lookup address or enter the address manually', async function () { + if (await this.page.locator('#field-select--addressSelect').isVisible()) { + await expect(this.page.locator('#field-select--addressSelect')).toBeVisible(); + + const optionToSelect = await this.page + .locator('option', { hasText: 'COMIC RELIEF, CAMELFORD HOUSE 87-90' }) + .textContent(); + + await this.page.locator('select#field-select--addressSelect').selectOption({ + label: optionToSelect, + }); + + await expect(this.page.locator('input#field-input--postcode')).toHaveValue('SE1 7TP'); + } else { + await this.page.locator('a[aria-describedby=field-error--addressDetails]').click(); + await this.page.locator('#field-input--address1').fill('COMIC RELIEF'); + await this.page.locator('#field-input--address2').fill('CAMELFORD HOUSE 87-90'); + await this.page.locator('#field-input--address3').fill('ALBERT EMBANKMENT'); + await this.page.locator('#field-input--town').fill('LONDON'); + } +}); diff --git a/playwright-local/tests/step-definitions/update/updateInternationalAddressesValidation.steps.js b/playwright-local/tests/step-definitions/update/updateInternationalAddressesValidation.steps.js new file mode 100644 index 00000000..92c8f98d --- /dev/null +++ b/playwright-local/tests/step-definitions/update/updateInternationalAddressesValidation.steps.js @@ -0,0 +1,24 @@ +const { When } = require('@cucumber/cucumber'); + +When('I enter the local update international address details', async function () { + await this.page.locator('#field-input--address1').fill('219 Beacon St'); + await this.page.locator('#field-input--address2').fill('Winder'); + await this.page.locator('#field-input--address3').fill('Park Ridge'); + await this.page.locator('#field-input--town').fill('GA'); +}); + +When('I select a random local update non-UK country', async function () { + const countryOptions = await this.page.$$eval('select#field-select--country>option', options => + options + .map(option => option.value) + .filter(value => value && value !== 'GB') + ); + + const randomCountryCode = countryOptions[Math.floor(Math.random() * countryOptions.length)]; + + await this.page.locator('select[name="country"]').selectOption({ + value: randomCountryCode, + }); + + await this.page.waitForTimeout(2000); +}); diff --git a/playwright-local/tests/submit/addressValidation.spec.js b/playwright-local/tests/submit/addressValidation.spec.js deleted file mode 100644 index 9779e779..00000000 --- a/playwright-local/tests/submit/addressValidation.spec.js +++ /dev/null @@ -1,66 +0,0 @@ -// @ts-check -const { test, expect } = require('@playwright/test'); - -test.describe('Address validation', () => { - - test.beforeEach(async ({ page }) => { - await page.goto('/', { timeout: 30000 }); - - await page.waitForLoadState('domcontentloaded'); - await page.locator('#field-label--giftaid').click(); - await page.locator('#field-input--mobile').fill('07123456789'); - await page.locator('input#field-input--firstname').fill('test'); - await page.locator('input#field-input--lastname').fill('user'); - }); - - test('empty postcode should show error message', async ({ page }) => { - await page.locator('input#field-input--postcode').fill(''); - await page.locator('button[type=submit]').click(); - await expect(page.locator('div#field-error--postcode > span')).toHaveText('Please enter your postcode'); - await page.close(); - }); - - test('invalid postcodes should show error messages', async ({ page }) => { - await page.locator('input#field-input--postcode').fill('12SE17TP'); - await expect(page.locator('div#field-error--postcode > span')).toHaveText('Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below.'); - await page.locator('input#field-input--postcode').fill('comic relief'); - await expect(page.locator('div#field-error--postcode > span')).toHaveText('Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below.'); - await page.locator('input#field-input--postcode').fill('cro 7tp'); - await expect(page.locator('div#field-error--postcode > span')).toHaveText('Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below.'); - await page.close(); - }); - - test('enter postcode but submit without selecting address should show error message', async ({ page }) => { - await page.locator('input#field-input--postcode').fill('SE1 7TP'); - await page.locator('#postcode_button').click(); - await expect(page.locator('#field-select--addressSelect')).toBeVisible(); - await page.locator('button[type=submit]').click(); - await expect(page.locator('div#field-error--addressSelect > span')).toHaveText('Please select your address'); - await page.close(); - }); - - test('clicking on manual address link should show address fields', async ({ page }) => { - await page.locator('input#field-input--postcode').fill('SE1 7TP'); - await expect(page.locator('a[aria-describedby=field-error--addressDetails]')).toBeVisible(); - await page.locator('a[aria-describedby=field-error--addressDetails]').click(); - await expect(page.locator('#field-input--address1')).toBeVisible(); - await expect(page.locator('#field-input--address2')).toBeVisible(); - await expect(page.locator('#field-input--address3')).toBeVisible(); - await expect(page.locator('#field-input--town')).toBeVisible(); - await expect(page.locator('select#field-select--country')).toBeVisible(); - await page.close(); - }); - - test('validate address fields', async ({ page }) => { - await page.locator('input#field-input--postcode').fill('SE1 7TP'); - await page.locator('a[aria-describedby=field-error--addressDetails]').click(); - - // Should see error message for address1 when inout with special characters is entered - await page.locator('#field-input--address1').fill('@£%3dComic Relief'); - await expect(page.locator('#field-error--address1 > span')).toHaveText("This field only accepts alphanumeric characters and ' . - & _ /"); - await page.locator('#field-input--town').fill(' Comic Relief'); - await expect(page.locator('#field-error--town > span')).toHaveText("This field only accepts alphanumeric characters and ' . - & _ /"); - - await page.close(); - }); -}); diff --git a/playwright-local/tests/submit/formValidation.spec.js b/playwright-local/tests/submit/formValidation.spec.js deleted file mode 100644 index b2e03ab5..00000000 --- a/playwright-local/tests/submit/formValidation.spec.js +++ /dev/null @@ -1,82 +0,0 @@ -// @ts-check -const { test, expect } = require('@playwright/test'); -const { Commands } = require('../utils/commands'); - -test.describe('Address validation', () => { - - test.beforeEach(async ({ page }) => { - await page.goto('/', { timeout: 30000 }); - await page.waitForLoadState('domcontentloaded'); - await page.locator('#field-label--giftaid').click(); - }); - - test('Validate mobile number field', async ({ page }) => { - const commands = new Commands(page); - - // Test cases for various mobile number validations - const mobileTestCases = [ - { input: '0712345678', error: 'Please enter a valid mobile phone number - it must be the same number associated with your donation.' }, - { input: '0712345678900', error: 'Please enter a valid mobile phone number - it must be the same number associated with your donation.' }, - { input: '0712 345 6789', error: 'Please enter a valid mobile phone number - it must be the same number associated with your donation.' }, - { input: '0780ab5694245', error: 'Please enter a valid mobile phone number - it must be the same number associated with your donation.' }, - ]; - - for (let testCase of mobileTestCases) { - await page.locator('#field-input--mobile').fill(''); // Clear the field before each test - await page.locator('#field-input--mobile').type(testCase.input, { delay: 100 }); - await expect(page.locator('div#field-error--mobile > span')).toHaveText(testCase.error); - } - - // Validate correct mobile number - await page.locator('#field-input--mobile').fill(''); // Ensure the field is cleared and filled with valid data - await commands.populateFormFields(page, { mobile: '07123456789' }); - await page.locator('button[type=submit]').click(); - await expect(page.locator('div > h1')).toHaveText('Thank you, test!'); - }); - - test('validate first name field', async ({ page }) => { - const commands = new Commands(page); - - // First name with invalid characters - await page.locator('#field-input--firstname').fill('Test^$%£'); - await expect(page.locator('div#field-error--firstname')).toHaveText('This field only accepts 25 alphabetic characters and \' - starting with alphabetic characters'); - - // First name with just a space - await page.locator('#field-input--firstname').fill(' '); - await expect(page.locator('div#field-error--firstname')).toHaveText('This field only accepts 25 alphabetic characters and \' - starting with alphabetic characters'); - - // First name with alphanumeric characters - await page.locator('#field-input--firstname').fill('123Test'); - await expect(page.locator('div#field-error--firstname')).toHaveText('This field only accepts 25 alphabetic characters and \' - starting with alphabetic characters'); - - // Validate correct first name - await page.locator('#field-input--firstname').fill(''); - await commands.populateFormFields(page, { firstName: 'testFirstname' }); - await page.locator('button[type=submit]').click(); - await expect(page.locator('div > h1')).toHaveText('Thank you, testFirstname!'); - }); - - test('validate last name field', async ({ page }) => { - const commands = new Commands(page); - - // Last name with invalid characters - await page.locator('#field-input--lastname').fill('Test^$%£'); - await expect(page.locator('div#field-error--lastname > span')).toHaveText('This field only accepts 25 alphanumeric characters and , . ( ) / & \' - starting with alphanumeric characters'); - - // Last name with just a space - await page.locator('#field-input--lastname').fill(' '); - await expect(page.locator('div#field-error--lastname > span')).toHaveText('This field only accepts 25 alphanumeric characters and , . ( ) / & \' - starting with alphanumeric characters'); - - // Last name with alphanumeric characters (valid case) - await page.locator('#field-input--lastname').fill('123Test'); - expect(await page.locator('div#field-error--lastname > span').count()).toEqual(0); - - // Validate correct last name - await page.locator('#field-input--lastname').fill(''); - await commands.populateFormFields(page); - await page.locator('button[type=submit]').click(); - await expect(page.locator('div > h1')).toContainText('Thank you,\n' + - 'test!'); - await page.close(); - }); -}); diff --git a/playwright-local/tests/submit/internationalAddressesValidation.spec.js b/playwright-local/tests/submit/internationalAddressesValidation.spec.js deleted file mode 100644 index ea20a39f..00000000 --- a/playwright-local/tests/submit/internationalAddressesValidation.spec.js +++ /dev/null @@ -1,49 +0,0 @@ -// @ts-check -const { test, expect } = require('@playwright/test'); -const { Commands } = require('../utils/commands'); - -test.describe('International addresses validation', () => { - test('selecting a non-UK country and entering a non-UK postcode should submit the form', async ({ page }) => { - - const commands = new Commands(page); - - await page.goto('/', { timeout: 30000 }); - - await page.waitForLoadState('domcontentloaded'); - - await page.waitForLoadState('domcontentloaded'); - - // Click to activate the form and fill in default values - await page.click('#field-label--giftaid'); - await page.fill('#field-input--mobile', '07123456789'); - await page.fill('input#field-input--firstname', 'test'); - await page.fill('input#field-input--lastname', 'user'); - await page.fill('input#field-input--postcode', '30916-395'); // Non-UK postcode - - // Verify error message for UK postcode requirement - await expect(page.locator('div#field-error--postcode > span')).toContainText('Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below.'); - - // Fill in address details - await page.click('a[aria-describedby=field-error--addressDetails]'); - await page.fill('#field-input--address1', '219 Beacon St'); - await page.fill('#field-input--address2', 'Winder'); - await page.fill('input#field-input--town', 'GA'); - - // Select a random non-UK country - const countries = await page.$$eval('select#field-select--country > option', options => options.map(option => option.value)); - const randomCountryCode = countries[Math.floor(Math.random() * countries.length)]; - await page.selectOption('select[name="country"]', { value: randomCountryCode }); - - // Verify that postcode error is resolved after country change - await expect(page.locator('div#field-error--postcode > span')).not.toBeVisible(); - - // Submit the form and ensure no errors are shown for international address - await commands.selectMarketingPrefs(page); // Assuming this handles checkbox interactions - await page.click('button[type=submit]'); - await expect(page.locator('div > h1')).toContainText('Thank you,\n' + - 'test!'); - await page.close(); - - await page.close(); - }); -}); diff --git a/playwright-local/tests/submit/marketingPreferencesValidation.spec.js b/playwright-local/tests/submit/marketingPreferencesValidation.spec.js deleted file mode 100644 index 4240b3da..00000000 --- a/playwright-local/tests/submit/marketingPreferencesValidation.spec.js +++ /dev/null @@ -1,78 +0,0 @@ -// @ts-check -const { test, expect } = require('@playwright/test'); -const { Commands } = require('../utils/commands'); - -const Chance = require('chance'); -const chance = new Chance(); - -const email = `giftaid-staging-${Date.now().toString()}@email.sls.comicrelief.com`; -const phone = chance.phone({ country: 'uk', mobile: true }).replace(/\s/g, ''); - -test.describe('Marketing preferences validation', () => { - - test.beforeEach(async ({ page }) => { - - const commands = new Commands(page); - await page.goto('/', { timeout: 30000 }); - await page.waitForLoadState('domcontentloaded'); - await page.click('#field-label--giftaid'); - await commands.populateFormFields(page); - }); - - test('clicking and unclicking marketing prefs options should submit the giftaid form', async ({ page }) => { - // Interact with marketing preferences - const marketingOptions = ['[aria-label="field-label--Email--Email"]', '[aria-label="field-label--Phone--Phone"]', '[aria-label="field-label--Text--SMS"]']; - for (const option of marketingOptions) { - await page.click(option); - expect(await page.locator(option).isChecked()).toBeTruthy(); - } - - // Enter email and phone to validate the form can still submit - await expect(page.locator('input#field-input--email')).toBeVisible(); - await page.fill('input#field-input--email', email); - await expect(page.locator('input#field-input--phone')).toBeVisible(); - await page.fill('input#field-input--phone', phone); - - // Submit the form - await page.click('button[type=submit]'); - await expect(page.locator('div > h1')).toHaveText('Thank you, test!'); - }); - - test('Validate email marketing preference field', async ({ page }) => { - await page.click('[aria-label="field-label--Email--Email"]'); - await page.fill('input#field-input--email', email); - - // Clear and check for error - await page.fill('input#field-input--email', ''); - await expect(page.locator('#field-error--email')).toHaveText('Please fill in your email address'); - - // Input invalid email and check for error - await page.fill('input#field-input--email', 'example@£$^&email.com'); - await expect(page.locator('#field-error--email')).toHaveText('Please fill in a valid email address'); - - // Re-enter valid email and submit - await page.fill('input#field-input--email', email); - await page.click('button[type=submit]'); - await expect(page.locator('div > h1')).toHaveText('Thank you, test!'); - }); - - test('Validate phone marketing preference field', async ({ page }) => { - await page.click('[aria-label="field-label--Phone--Phone"]'); - await page.fill('input#field-input--phone', phone); - - // Clear and check for error - await page.fill('input#field-input--phone', ''); - await expect(page.locator('div#field-error--phone > span')).toHaveText('Please fill in your phone number'); - - // Input invalid phone number and check for error - await page.fill('input#field-input--phone', '0208569424'); - await expect(page.locator('div#field-error--phone > span')).toHaveText('Please fill in a valid UK phone number, with no spaces'); - - // Re-enter valid phone number and submit - await page.fill('input#field-input--phone', phone); - await page.click('button[type=submit]'); - await expect(page.locator('div > h1')).toContainText('Thank you,\n' + - 'test!'); - await page.close(); - }); -}); diff --git a/playwright-local/tests/submit/postcodeLookup.spec.js b/playwright-local/tests/submit/postcodeLookup.spec.js deleted file mode 100644 index 63959b33..00000000 --- a/playwright-local/tests/submit/postcodeLookup.spec.js +++ /dev/null @@ -1,50 +0,0 @@ -// @ts-check -const { test, expect } = require('@playwright/test'); - -test.describe('Postcode validation', () => { - - test.beforeEach(async ({ page }) => { - await page.goto('/', { timeout: 30000 }); - - await page.waitForLoadState('domcontentloaded'); - await page.click('#field-label--giftaid'); - await page.fill('#field-input--mobile', '07123456789'); - await page.fill('input#field-input--firstname', 'test'); - await page.fill('input#field-input--lastname', 'user'); - }); - - test('Postcode formatting errors', async ({ page }) => { - const postcodes = [ - { code: 'S E 1 7 T P', message: 'Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below.' }, - { code: 'SE$%TP', message: 'Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below.' } - ]; - - for (const { code, message } of postcodes) { - await page.fill('input#field-input--postcode', code); - await expect(page.locator('div#field-error--postcode > span')).toBeVisible(); - await expect(page.locator('div#field-error--postcode > span')).toContainText(message); - } - await page.close(); - }); - - test('enter valid UK postcode using postcode lookup should be able to submit the form', async ({ page }) => { - await page.fill('input#field-input--postcode', 'SE1 7TP'); - await page.click('#postcode_button'); - - if (await page.isVisible('#field-select--addressSelect')) { - await page.selectOption('select#field-select--addressSelect', { label: 'COMIC RELIEF, CAMELFORD HOUSE 87-90' }); - await expect(page.locator('input#field-input--postcode')).toHaveValue('SE1 7TP'); - } else { - await page.click('a[aria-describedby=field-error--addressDetails]'); - await page.fill('#field-input--address1', 'COMIC RELIEF'); - await page.fill('#field-input--address2', 'CAMELFORD HOUSE 87-90'); - await page.fill('#field-input--address3', 'ALBERT EMBANKMENT'); - await page.fill('#field-input--town', 'LONDON'); - } - - await page.click('button[type=submit]'); - await expect(page.locator('div > h1')).toContainText('Thank you,\n' + 'test!'); - - await page.close(); - }); -}); diff --git a/playwright-local/tests/submit/validFormSubmission.spec.js b/playwright-local/tests/submit/validFormSubmission.spec.js deleted file mode 100644 index 59c675dc..00000000 --- a/playwright-local/tests/submit/validFormSubmission.spec.js +++ /dev/null @@ -1,32 +0,0 @@ -// @ts-check -const { test, expect } = require('@playwright/test'); -const { Commands } = require('../utils/commands'); - -test('Valid giftaid submission', async ({ page }) => { - - const commands = new Commands(page); - - await page.goto('/', { timeout: 30000 }); - await page.waitForLoadState('domcontentloaded'); - - // Click the Giftaid checkbox - await page.click('#field-label--giftaid'); - - // Populate all the form fields with valid inputs - await commands.populateFormFields(page); - - // Select marketing preferences as required - await commands.selectMarketingPrefs(page); - - // Submit the form and wait for the navigation to ensure the submission goes through - await Promise.all([ - page.waitForNavigation(), // This ensures that the navigation happens before the check - page.click('button[type=submit]') - ]); - - // Check for the thank you message to confirm successful submission - await expect(page.locator('div > h1')).toContainText('Thank you,\n' + - 'test!'); - - await page.close(); -}); diff --git a/playwright-local/tests/support/hooks.js b/playwright-local/tests/support/hooks.js new file mode 100644 index 00000000..608602ae --- /dev/null +++ b/playwright-local/tests/support/hooks.js @@ -0,0 +1,60 @@ +require('dotenv').config(); + +const { Before, After, setDefaultTimeout } = require('@cucumber/cucumber'); +const { chromium, webkit, devices } = require('@playwright/test'); // imports playwright browsers and devices +const { Commands } = require('../utils/commands'); + +setDefaultTimeout(300 * 1000); // 5 mins + +// Runs before each scenario +Before(async function () { + const browserName = process.env.BROWSER || 'chromium'; + const isMobileSafari = browserName === 'mobile-safari'; + + this.browser = isMobileSafari // Launches WebKit for mobile Safari + ? await webkit.launch({ + headless: process.env.HEADED !== 'true', // Checks whether to run mobile Safari mode + }) + : await chromium.launch({ + channel: 'chrome', + headless: process.env.HEADED !== 'true', // Runs headless by default + }); + + // Creates a fresh isolated browser session for each scenario + this.context = await this.browser.newContext( + isMobileSafari + ? { + ...devices['iPhone 12'], + serviceWorkers: 'block', // Blocks service workers to reduce caching/flaky behaviour + } + : { + viewport: { + width: 1300, + height: 1000, + }, + serviceWorkers: 'block', + } + ); + + this.page = await this.context.newPage(); + + // Increase navigation timeout for slower redirects/page loads + this.page.setDefaultNavigationTimeout(45000); + + this.commands = new Commands(this.page); // Creates a new browser tab +}); + +// Runs after each scenario +After(async function () { + if (this.page) { + await this.page.close(); + } + // Close context and browser + if (this.context) { + await this.context.close(); + } + + if (this.browser) { + await this.browser.close(); + } +}); diff --git a/playwright-local/tests/support/world.js b/playwright-local/tests/support/world.js new file mode 100644 index 00000000..bc162f1f --- /dev/null +++ b/playwright-local/tests/support/world.js @@ -0,0 +1,14 @@ +// This defines shared context for each scenario +// Values stored here (browser, page, commands) are accessible across all step definitions via "this" +const { setWorldConstructor } = require('@cucumber/cucumber'); + +class CustomWorld { + constructor() { + this.browser = null; + this.context = null; + this.page = null; + this.commands = null; + } +} + +setWorldConstructor(CustomWorld); diff --git a/playwright-local/tests/update/formValidation.spec.js b/playwright-local/tests/update/formValidation.spec.js deleted file mode 100644 index 7fee95a5..00000000 --- a/playwright-local/tests/update/formValidation.spec.js +++ /dev/null @@ -1,313 +0,0 @@ -// @ts-check -const { test, expect } = require('@playwright/test'); -const { Commands } = require('../utils/commands'); -const Chance = require('chance'); -const chance = new Chance(); - -const email = `giftaid-staging-${Date.now().toString()}@email.sls.comicrelief.com`; - -test.describe('Giftaid update form validation', () => { - - test.beforeEach(async ({ page }) => { - await page.goto('/update', { timeout: 30000 }); - - await page.waitForLoadState('domcontentloaded'); - }); - - test('empty input fields should show error messages', async ({ page }) => { - - // submit the form - await page.locator('button[type=submit]').click(); - - await expect(page.locator('div#field-error--firstname > span')).toContainText('Please fill in your first name'); - await expect(page.locator('div#field-error--lastname > span')).toContainText('Please fill in your last name'); - await expect(page.locator('div#field-error--postcode > span')).toContainText('Please enter your postcode'); - await expect(page.locator('div#field-error--addressDetails > span')).toContainText('Please fill in your address'); - - // click on manual address link to show address fields error messages - await page.locator('a[aria-describedby=field-error--addressDetails]').click(); - await expect(page.locator('div#field-error--address1 > span')).toContainText('Please fill in your address line 1'); - await expect(page.locator('div#field-error--town > span')).toContainText('Please fill in your town/city'); - - // giftaid declaration error message - await expect(page.locator('div#field-error--giftAidClaimChoice > span')).toContainText('This field is required'); - - await page.close(); - }); - - - test('validate first name field on giftaid update form', async ({ page }) => { - - const commands = new Commands(page); - - await page.locator('#field-input--firstname').fill('test'); - await page.locator('#field-input--firstname').fill(''); - await expect(page.locator('#field-error--firstname')).toContainText('Please fill in your first name'); - - // enter firstname field with special chars should show error message - await page.locator('#field-input--firstname').type('Test^$%£'); - await expect(page.locator('#field-error--firstname')).toContainText('This field only accepts alphabetic characters and \' -\n'); - - // firstname with just a space should show error message - await page.keyboard.press('Backspace'); - await page.locator('#field-input--firstname').type(' '); - await expect(page.locator('#field-error--firstname')).toContainText('This field only accepts alphabetic characters and \' -\n'); - - // firstname with a mixture of alphanumeric chars should show error message - await page.locator('#field-input--firstname').fill(''); // clear the first-name field - await page.locator('#field-input--firstname').type('123Test'); - await expect(page.locator('#field-error--firstname')).toContainText('This field only accepts alphabetic characters and \' -\n'); - - // clear the first name field - await page.locator('#field-input--firstname').fill(''); - - // entering valid input fields should be able to submit the form - await commands.populateUpdateFormFields(page); - - // select giftaid declaration - await page.locator('#giftAidClaimChoice>div:nth-child(2)>label').click(); - - // submit the form - await page.locator('button[type=submit]').click(); - - await expect(page.locator('div > h1')).toContainText('Thank you,\n' + - 'test!'); - - await page.close(); - }); - - test('validate last name field on giftaid update form', async ({ page }) => { - - const commands = new Commands(page); - - await page.locator('#field-input--lastname').fill('test lastname'); - await page.locator('#field-input--lastname').fill(''); - await expect(page.locator('div#field-error--lastname > span')).toContainText('Please fill in your last name'); - - // enter lastname field with special chars should show error message - await page.locator('#field-input--lastname').type('Test^$%£'); - await expect(page.locator('div#field-error--lastname > span')).toContainText('This field only accepts alphanumeric characters and , . ( ) / & \' -'); - - // lastname with just a space should show error message - await page.keyboard.press('Backspace'); - await page.locator('#field-input--lastname').type(' '); - await expect(page.locator('div#field-error--lastname > span')).toContainText('This field only accepts alphanumeric characters and , . ( ) / & \' -'); - - // lastname with a mixture of alphanumeric chars should not show error message - await page.locator('#field-input--lastname').fill(''); // clear the last-name field - await page.locator('#field-input--lastname').type('123Test'); - // should not show error message - expect(await page.locator('div#field-error--lastname > span').count()).toEqual(0); - - // clear the last name field - await page.locator('#field-input--lastname').fill(''); - - // entering valid input fields should be able to submit the form - await commands.populateUpdateFormFields(page); - - // select giftaid declaration - await page.locator('#giftAidClaimChoice>div:nth-child(2)>label').click(); - - // submit the form - await page.locator('button[type=submit]').click(); - - await expect(page.locator('div > h1')).toContainText('Thank you,\n' + - 'test!'); - - await page.close(); - - }); - - test('validate email field on giftaid update form', async ({ page }) => { - - const commands = new Commands(page); - - // email validation - // email that has $ after the domian name should show error message - await page.locator('input#field-input--email').fill('test@comic$relief.com'); - await expect(page.locator('div#field-error--email > span')).toContainText('Please fill in a valid email address'); - - // email that has @ after the domian name should show error message - await page.locator('input#field-input--email').fill(''); // clear the email field - await page.locator('input#field-input--email').type('test@c{(micrelief.com'); - await expect(page.locator('div#field-error--email > span')).toContainText('Please fill in a valid email address'); - - // email that has % after the domian name should show error message - await page.locator('input#field-input--email').fill(''); - await page.locator('input#field-input--email').type('test@comic%relief.com'); - await expect(page.locator('div#field-error--email > span')).toContainText('Please fill in a valid email address'); - - // email that has special chars $%^ before domain name should not show error message - await page.locator('input#field-input--email').fill(''); - await page.locator('input#field-input--email').type('te$%^st@comicrelief.com'); - await expect(page.locator('div#field-error--email > span')).not.toBeVisible(); - - // email that has mix of special chars that's valid and not valid should show error message - await page.locator('input#field-input--email').fill(''); - await page.locator('input#field-input--email').type('Test0-9!#$%&\'*+/=?^_{|}~-@comicrelief_9-8.com.uk'); - await expect(page.locator('div#field-error--email > span')).toContainText('Please fill in a valid email address'); - - // clear the email field - await page.locator('input#field-input--email').fill(''); - - // entering valid input fields should be able to submit the form - await commands.populateUpdateFormFields(page); - - // select giftaid declaration - await page.locator('#giftAidClaimChoice>div:nth-child(2)>label').click(); - - // submit the form - await page.locator('button[type=submit]').click(); - - await expect(page.locator('div > h1')).toContainText('Thank you,\n' + - 'test!'); - - await page.close(); - }); - - test('Validate mobile number field', async ({ page }) => { - const commands = new Commands(page); - // List of allowed prefixes for UK mobile numbers - const prefixes = ['071', '073', '074', '075', '077', '078', '079']; - // Randomly select one prefix from the list - const prefix = chance.pickone(prefixes); - // Generate the remaining 8 digits randomly - const mobile = `${prefix}${chance.string({ pool: '0123456789', length: 8 })}`; - console.log('mobile number generated', mobile); - - // Test cases for various mobile number validations - const mobileTestCases = [ - { input: '0722345678', error: 'Please enter a valid mobile phone number - it must be the same number associated with your donation.' }, - { input: '0722345678900', error: 'Please enter a valid mobile phone number - it must be the same number associated with your donation.' }, - { input: '0722 345 6789', error: 'Please enter a valid mobile phone number - it must be the same number associated with your donation.' }, - { input: '0780ab5694245', error: 'Please enter a valid mobile phone number - it must be the same number associated with your donation.' }, - { input: '07123456789', valid: true }, - { input: '07340707252', valid: true }, - ]; - - for (let testCase of mobileTestCases) { - await page.locator('#field-input--mobile').fill(''); // Clear the field before each test - await page.locator('#field-input--mobile').type(testCase.input, { delay: 100 }); - if (testCase.valid) { - await expect(page.locator('div#field-error--mobile > span')).not.toBeVisible(); - } else { - await expect(page.locator('div#field-error--mobile > span')).toHaveText(testCase.error); - } - } - - // Validate correct mobile number - await page.locator('#field-input--mobile').fill(''); // Ensure the field is cleared before filling with valid data - - await commands.populateUpdateFormFields(page, { mobile: mobile }); - - // Select yes for giftaid declaration to complete the form - await page.locator('#giftAidClaimChoice>div:nth-child(2)>label').click(); - - await page.locator('button[type=submit]').click(); - await expect(page.locator('div > h1')).toHaveText('Thank you, test!'); - }); - - test('postcode entered with extra spaces should show error message', async ({ page }) => { - - await page.locator('input#field-input--postcode').type('S E 1 7 T P'); - await expect(page.locator('div#field-error--postcode > span')).toBeVisible(); - await expect(page.locator('div#field-error--postcode > span')).toContainText('Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below.'); - - await page.close(); - }); - - test('compact lowercase and no-space UK postcodes do not show format error (aligned with data-models / postcode)', async ({ page }) => { - await page.locator('input#field-input--postcode').fill('se17tp'); - await expect(page.locator('div#field-error--postcode > span')).not.toBeVisible(); - await page.locator('input#field-input--postcode').fill('SE17TP'); - await expect(page.locator('div#field-error--postcode > span')).not.toBeVisible(); - await page.close(); - }); - - test('postcode entered with special characters should show error message', async ({ page }) => { - - await page.locator('input#field-input--postcode').type('SE$%TP'); - await expect(page.locator('div#field-error--postcode > span')).toBeVisible(); - await expect(page.locator('div#field-error--postcode > span')).toContainText('Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below.'); - - await page.close(); - }); - - test('postcode with incorrect format like "cro 7tp" should show error message', async ({ page }) => { - - await page.locator('input#field-input--postcode').type('cro 7tp'); - await expect(page.locator('div#field-error--postcode > span')).toBeVisible(); - await expect(page.locator('div#field-error--postcode > span')).toContainText('Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below.'); - - await page.close(); - }); - - test('enter valid UK postcode on giftaid update form using postcode lookup should be able to submit the form', async ({ page }) => { - - // fill in all input fields - await page.locator('#field-input--firstname').fill('test'); - await page.locator('#field-input--lastname').fill('test lastname'); - await page.locator('input#field-input--email').fill('giftaid-staging-@email.sls.comicrelief.com'); - - // enter postcode - await page.locator('input#field-input--postcode').fill('SE1 7TP'); - // click on postcode lookup button - await page.locator('#postcode_button').click(); - - if (await page.locator('#field-select--addressSelect').isVisible()) { - console.log('postcode lookup address dropdown present select the address'); - - await expect(page.locator('#field-select--addressSelect')).toBeVisible(); - - await page.waitForSelector('select#field-select--addressSelect'); - - const optionToSelect = await page.locator('option', { hasText: 'COMIC RELIEF, CAMELFORD HOUSE 87-90' }).textContent(); - console.log('selected option: ', optionToSelect); - - // Use option text to select - await page.locator('select#field-select--addressSelect').selectOption({ label: optionToSelect }); - - // expect pre-enetered se17tp postcode to change to SE1 7TP when address is selected by removing the extra spaces - await expect(page.locator('input#field-input--postcode')).toHaveValue('SE1 7TP'); - - const addressLine1 = await page.evaluate(() => document.querySelector('#field-input--address1').getAttribute('value')); - console.log('Address line 1 field value is : ', addressLine1); - - const addressLine2 = await page.evaluate(() => document.querySelector('#field-input--address2').getAttribute('value')); - console.log('Address line 1 field value is : ', addressLine2); - - const addressLine3 = await page.evaluate(() => document.querySelector('#field-input--address3').getAttribute('value')); - console.log('Address line 1 field value is : ', addressLine3); - - const town = await page.evaluate(() => document.querySelector('input#field-input--town').getAttribute('value')); - console.log('Address line 1 field value is : ', town); - - // select giftaid declaration - await page.locator('#giftAidClaimChoice>div:nth-child(2)>label').click(); - - // clicking on submit button should show error on address lookup - await page.locator('button[type=submit]').click(); - - await expect(page.locator('div > h1')).toContainText('Thank you,\n' + - 'test!'); - } else { - - // click on manual address link - await page.locator('a[aria-describedby=field-error--addressDetails]').click(); - await page.locator('#field-input--address1').type('COMIC RELIEF'); - await page.locator('#field-input--address2').type('CAMELFORD HOUSE 87-90'); - await page.locator('#field-input--address3').type('ALBERT EMBANKMENT'); - await page.locator('#field-input--town').type('LONDON'); - - // select giftaid declaration - await page.locator('#giftAidClaimChoice>div:nth-child(2)>label').click(); - - // clicking on submit button should show error on address lookup - await page.locator('button[type=submit]').click(); - - await expect(page.locator('div > h1')).toContainText('Thank you,\n' + - 'test!'); - } - await page.close(); - }); -}); diff --git a/playwright-local/tests/update/internationalAddressesValidation.spec.js b/playwright-local/tests/update/internationalAddressesValidation.spec.js deleted file mode 100644 index 1d4dd934..00000000 --- a/playwright-local/tests/update/internationalAddressesValidation.spec.js +++ /dev/null @@ -1,52 +0,0 @@ -// @ts-check -const { test, expect } = require('@playwright/test'); -const { v4: uuidv4 } = require('uuid'); - -test.describe('International addresses validation on update form', () => { - test('selecting a non-UK country and entering a non-UK postcode should submit the update form', async ({ page }) => { - - await page.goto('/update', { timeout: 30000 }); - - await page.waitForLoadState('domcontentloaded'); - - /// fill in all input fields - await page.locator('#field-input--firstname').fill('test'); - await page.locator('#field-input--lastname').fill('test lastname'); - await page.locator('input#field-input--email').fill('giftaid-staging-@email.sls.comicrelief.com'); - - // enter a non-UK postcode and attempt to validate it - await page.locator('input#field-input--postcode').fill('30916-395'); - await expect(page.locator('div#field-error--postcode > span')).toContainText('Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below.'); - - // manually enter international address details - await page.locator('a[aria-describedby=field-error--addressDetails]').click(); - await page.locator('#field-input--address1').fill('219 Beacon St'); - await page.locator('#field-input--address2').fill('Winder'); - await page.locator('#field-input--address3').fill('Park Ridge'); - await page.locator('#field-input--town').fill('GA'); - - // Select a random country from the dropdown (excluding UK to simulate international address) - const countryOptions = await page.$$eval('select#field-select--country>option', options => - options.map(option => option.value).filter(value => value !== 'GB') - ); - const randomCountryCode = countryOptions[Math.floor(Math.random() * countryOptions.length)]; - await page.locator('select[name="country"]').selectOption({ value: randomCountryCode }); - - // Wait for the form to adjust to the selected country - await page.waitForTimeout(2000); - - // When an international country is selected, the postcode error for UK format should not show anymore - await expect(page.locator('div#field-error--postcode > span')).not.toBeVisible(); - - // Select yes for giftaid declaration to complete the form - await page.locator('#giftAidClaimChoice>div:nth-child(2)>label').click(); - - // Submitting the form with valid international details - await page.locator('button[type=submit]').click(); - - // Thank you message on success page - await expect(page.locator('div > h1')).toContainText('Thank you,\n' + - 'test!'); - await page.close(); - }); -}); diff --git a/playwright-local/tests/update/validFormSubmission.spec.js b/playwright-local/tests/update/validFormSubmission.spec.js deleted file mode 100644 index ad4b59cd..00000000 --- a/playwright-local/tests/update/validFormSubmission.spec.js +++ /dev/null @@ -1,25 +0,0 @@ -// @ts-check -const { test, expect } = require('@playwright/test'); -const { Commands } = require('../utils/commands'); - -test('Valid Giftaid Update submission', async ({ page }) => { - - const commands = new Commands(page); - - await page.goto('/update', { timeout: 30000 }); - - await page.waitForLoadState('domcontentloaded'); - - // Populate all input fields with valid data - await commands.populateUpdateFormFields(page); - - // Select 'Yes' for GiftAid declaration - await page.locator('#giftAidClaimChoice>div:nth-child(2)>label').click(); - - // Submit the form and validate the thank you message - await page.locator('button[type=submit]').click(); - await expect(page.locator('div > h1')).toContainText('Thank you,\n' + - 'test!'); - - await page.close(); -}); diff --git a/playwright-staging/browserstack.js b/playwright-staging/browserstack.js deleted file mode 100644 index 1ea622eb..00000000 --- a/playwright-staging/browserstack.js +++ /dev/null @@ -1,92 +0,0 @@ -require('dotenv').config(); - -const base = require('@playwright/test'); -const clientPlaywrightVersion = require('@playwright/test/package.json').version; // "1.50.0" - -// BrowserStack Specific Capabilities. -const caps = { - project: 'giftaid-react', - name: 'e2e tests', - browser: 'chrome', - browser_version: 'latest', - resolution: '1920x1080', - os: 'Windows', - os_version: '11', - 'browserstack.username': process.env.BROWSERSTACK_USERNAME, - 'browserstack.accessKey': process.env.BROWSERSTACK_ACCESS_KEY, - 'client.playwrightVersion': clientPlaywrightVersion, - 'browserstack.playwrightVersion': clientPlaywrightVersion, - - // logs - 'browserstack.networkLogs': true, - 'browserstack.console': 'info', - 'browserstack.debug': true, - 'browserstack.idleTimeout': 300, -}; - -// Patching the capabilities dynamically according to the project name. -const patchCaps = (name, title) => { - const combination = name.split(/@browserstack/)[0]; - const [browserCaps, rawOsCaps] = combination.split(':'); - let [browser, browser_version] = (browserCaps || '').split('@'); - - browser = (browser || 'chrome').toLowerCase(); - if (browser === 'chromium') browser = 'chrome'; - - const osCaps = (rawOsCaps || '').trim(); - const osTokens = osCaps ? osCaps.split(/\s+/) : []; - const os = osTokens.shift() || 'Windows'; - const os_version = osTokens.join(' ') || '11'; - - caps.browser = browser; - caps.browser_version = browser_version || 'latest'; - caps.os = os; - caps.os_version = os_version; - caps.name = title; -}; - -const isHash = (entity) => Boolean(entity && typeof(entity) === "object" && !Array.isArray(entity)); -const nestedKeyValue = (hash, keys) => keys.reduce((hash, key) => (isHash(hash) ? hash[key] : undefined), hash); -const isUndefined = val => (val === undefined || val === null || val === ''); -const evaluateSessionStatus = (status) => { - if (!isUndefined(status)) { - status = status.toLowerCase(); - } - if (status === "passed") { - return "passed"; - } else if (status === "failed" || status === "timedout") { - return "failed"; - } else { - return ""; - } -} - -exports.test = base.test.extend({ - page: async ({ page, playwright }, use, testInfo) => { - // Use BrowserStack Launched Browser according to capabilities for cross-browser testing. - if (testInfo.project.name.match(/browserstack/)) { - patchCaps(testInfo.project.name,`${testInfo.title}`); - const vBrowser = await playwright.chromium.connect({ - wsEndpoint: - `wss://cdp.browserstack.com/playwright?caps=` + - `${encodeURIComponent(JSON.stringify(caps))}`, - }); - const vContext = await vBrowser.newContext(testInfo.project.use); - const vPage = await vContext.newPage(); - await use(vPage); - const testResult = { - action: 'setSessionStatus', - arguments: { - status: evaluateSessionStatus(testInfo.status), - reason: nestedKeyValue(testInfo, ['error', 'message']) - }, - }; - await vPage.evaluate(() => {}, - `browserstack_executor: ${JSON.stringify(testResult)}`); - await vPage.close(); - await vBrowser.close(); - } else { - use(page); - } - }, -}); diff --git a/playwright-staging/config/browserstack.js b/playwright-staging/config/browserstack.js new file mode 100644 index 00000000..8f629823 --- /dev/null +++ b/playwright-staging/config/browserstack.js @@ -0,0 +1,26 @@ +// Get Playwright version to match BrowserStack runtime +const clientPlaywrightVersion = require('@playwright/test/package.json').version; + +const caps = { + project: 'giftaid-react', + name: 'e2e tests', + browser: 'chrome', + browser_version: 'latest', + resolution: '1920x1080', + os: 'Windows', + os_version: '11', + 'browserstack.username': process.env.BROWSERSTACK_USERNAME, + 'browserstack.accessKey': process.env.BROWSERSTACK_ACCESS_KEY, + + // Match BrowserStack Playwright version with local version + 'client.playwrightVersion': clientPlaywrightVersion, + 'browserstack.playwrightVersion': clientPlaywrightVersion, + + // BrowserStack logs/debugging + 'browserstack.networkLogs': true, + 'browserstack.console': 'info', + 'browserstack.debug': true, + 'browserstack.idleTimeout': 300, +}; + +module.exports = caps; diff --git a/playwright-staging/config/cucumber.js b/playwright-staging/config/cucumber.js new file mode 100644 index 00000000..da890dbd --- /dev/null +++ b/playwright-staging/config/cucumber.js @@ -0,0 +1,15 @@ +module.exports = { + default: { + paths: ['tests/features/**/*.feature'], + require: [ + 'tests/support/**/*.js', + 'tests/step-definitions/**/*.js', + ], + format: ['progress', 'summary'], + // Use 'pretty' locally for readable step-by-step output. + // On CI keep the output minimal to avoid noisy logs. + retry: process.env.CI ? 2 : 0, // no retries when running locally + parallel: 3, + publishQuiet: true, // Hide the default Cucumber report publishing link (we check CI instead) + }, +}; diff --git a/playwright-staging/package.json b/playwright-staging/package.json index 82272c49..7573fff9 100644 --- a/playwright-staging/package.json +++ b/playwright-staging/package.json @@ -8,6 +8,7 @@ "devDependencies": { "@comicrelief/data-models": "^1.29.6", "@comicrelief/test-utils": "^1.5.15", + "@cucumber/cucumber": "8.9.1", "@playwright/test": "1.56.1", "axios": "^0.21.1", "chance": "^1.1.7", @@ -16,6 +17,6 @@ "uuid": "8.3.2" }, "scripts": { - "test:sanity": "FORCE_COLOR=1 playwright test --grep '@sanity'" + "test:sanity": "FORCE_COLOR=1 cucumber-js --config config/cucumber.js --tags '@sanity'" } } diff --git a/playwright-staging/playwright.config.js b/playwright-staging/playwright.config.js deleted file mode 100644 index 818d0dd5..00000000 --- a/playwright-staging/playwright.config.js +++ /dev/null @@ -1,45 +0,0 @@ -// @ts-check -const { devices } = require('@playwright/test'); - -const config = { - testDir: 'tests', - testMatch: '**/*.spec.js', - /* Maximum time one test can run for. */ - timeout: 300 * 1000, - expect: { - /** - * Maximum time expect() should wait for the condition to be met. - * For example in `await expect(locator).toHaveText();` - */ - timeout: 30 * 1000, - }, - reporter: 'list', - retries: 2, - workers: 3, - use:{ - viewport: null, - /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ - actionTimeout: 0, - /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ - trace: 'retain-on-failure', - navigationTimeout: 45000, - scriptTimeout: 60000, // this is needed for long running scripts - serviceWorkers: 'block', // optional but reduces flakiness - }, - grep: [new RegExp('@sanity')], - - /* Configure projects for major browsers */ - projects: [ - // -- BrowserStack Projects -- - // name should be of the format browser@browser_version:os os_version@browserstack - { - name: 'chrome@latest:Windows 11@browserstack', - use: { - browserName: 'chromium', - ...devices['Desktop Chrome'], - }, - }, - ], -}; - -module.exports = config; diff --git a/playwright-staging/tests/features/submit/addressValidation.feature b/playwright-staging/tests/features/submit/addressValidation.feature new file mode 100644 index 00000000..1a0f4233 --- /dev/null +++ b/playwright-staging/tests/features/submit/addressValidation.feature @@ -0,0 +1,44 @@ +@sanity @address-validation +Feature: Address validation + + Background: + Given I am on the Giftaid page + And I select the Giftaid option + And I enter the supporter details + + Scenario: Empty postcode should show an error message + When I enter the postcode "E1 8QS" + And I clear the postcode field + And I submit the Giftaid form + Then I should see the postcode error message "Please enter your postcode" + + Scenario Outline: Invalid postcodes should show error messages + When I enter the postcode "" + Then I should see the postcode error message "" + + Examples: + | postcode | message | + | 12SE17TP | Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below. | + | comic relief | Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below. | + | cro 7tp | Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below. | + + Scenario: Entering a postcode without selecting an address should show an error message + When I enter the postcode "E1 8QS" + And I search for the postcode + Then I should see the address dropdown + When I submit the Giftaid form + Then I should see the address select error message "Please select your address" + + Scenario: Clicking the manual address link should show the address fields + When I enter the postcode "E1 8QS" + Then I should see the manual address link + When I click the manual address link + Then I should see the manual address fields + + Scenario: Invalid address fields should show error messages + When I enter the postcode "E1 8QS" + And I click the manual address link + And I enter an invalid address line 1 + Then I should see the address line 1 error message + When I enter an invalid town + Then I should see the town error message diff --git a/playwright-staging/tests/features/submit/formValidation.feature b/playwright-staging/tests/features/submit/formValidation.feature new file mode 100644 index 00000000..860671d3 --- /dev/null +++ b/playwright-staging/tests/features/submit/formValidation.feature @@ -0,0 +1,55 @@ +@sanity @form-validation +Feature: Giftaid form validation + + Background: + Given I am on the Giftaid page + And I select the Giftaid option + + Scenario Outline: Invalid mobile numbers should show an error message + When I enter the mobile number "" + Then I should see the mobile error message "" + + Examples: + | mobile | message | + | 0712345678 | Please enter a valid mobile phone number - it must be the same number associated with your donation. | + | 0712345678900 | Please enter a valid mobile phone number - it must be the same number associated with your donation. | + | 0712 345 6789 | Please enter a valid mobile phone number - it must be the same number associated with your donation. | + | 0780ab5694245 | Please enter a valid mobile phone number - it must be the same number associated with your donation. | + + Scenario: A valid mobile number should submit the form + When I complete the Giftaid form with the mobile number "07123456789" + And I submit the Giftaid form + Then I should see the Giftaid thank you message + + Scenario Outline: Invalid first name values should show an error message + When I enter the first name "" + Then I should see the first name error message "" + + Examples: + | firstName | message | + | Test^$%£ | This field only accepts 25 alphabetic characters and ' - starting with alphabetic characters | + | SPACE | This field only accepts 25 alphabetic characters and ' - starting with alphabetic characters | + | 123Test | This field only accepts 25 alphabetic characters and ' - starting with alphabetic characters | + + Scenario: A valid first name should submit the form + When I complete the Giftaid form with the first name "testFirstname" + And I submit the Giftaid form + Then I should see the thank you message for "testFirstname" + + Scenario Outline: Invalid last name values should show an error message + When I enter the last name "" + Then I should see the last name error message "" + + Examples: + | lastName | message | + | Test^$%£ | This field only accepts 25 alphanumeric characters and , . ( ) / & ' - starting with alphanumeric characters | + | SPACE | This field only accepts 25 alphanumeric characters and , . ( ) / & ' - starting with alphanumeric characters | + + Scenario: An alphanumeric last name should not show an error message + When I enter the last name "123Test" + Then I should not see the last name error message + + Scenario: A valid last name should submit the form + When I complete the Giftaid form with valid details + And I submit the Giftaid form + Then I should see the Giftaid thank you message diff --git a/playwright-staging/tests/features/submit/internationalAddressesValidation.feature b/playwright-staging/tests/features/submit/internationalAddressesValidation.feature new file mode 100644 index 00000000..ab352e38 --- /dev/null +++ b/playwright-staging/tests/features/submit/internationalAddressesValidation.feature @@ -0,0 +1,15 @@ +@sanity @international +Feature: International address validation + + Scenario: Selecting a non-UK country and entering a non-UK postcode should submit the form + Given I am on the Giftaid page + And I select the Giftaid option + And I enter the supporter details + When I enter a non UK postcode + Then I should see the postcode validation error for UK format + When I enter the international address details manually + And I select a non UK country + Then the postcode error should disappear + When I select the marketing preferences + And I submit the Giftaid form + Then I should see the Giftaid thank you message diff --git a/playwright-staging/tests/features/submit/marketingPreferencesData.feature b/playwright-staging/tests/features/submit/marketingPreferencesData.feature new file mode 100644 index 00000000..84134e89 --- /dev/null +++ b/playwright-staging/tests/features/submit/marketingPreferencesData.feature @@ -0,0 +1,11 @@ +@sanity @marketing-preferences-data +Feature: Giftaid marketing preferences contact-store verification + + Scenario: Verify giftaid marketing preferences data in contact-store + Given I am on the Giftaid page + And I select the Giftaid option + When I populate the Giftaid form with the supporter details + And I select the marketing preferences + And I submit the Giftaid form + Then I should see the supporter thank you message + And the marketing preferences data should be stored in the contact-store diff --git a/playwright-staging/tests/features/submit/marketingPreferencesValidation.feature b/playwright-staging/tests/features/submit/marketingPreferencesValidation.feature new file mode 100644 index 00000000..4fd6c5e5 --- /dev/null +++ b/playwright-staging/tests/features/submit/marketingPreferencesValidation.feature @@ -0,0 +1,35 @@ +@sanity @marketing-preferences +Feature: Marketing preferences validation + + Background: + Given I am on the Giftaid page + And I select the Giftaid option + And I complete the Giftaid form with valid details + + Scenario: Clicking marketing preference options should submit the Giftaid form + When I select all the marketing preference options + And I enter the valid marketing preference contact details + And I submit the Giftaid form + Then I should see the Giftaid thank you message + + Scenario: Email marketing preference field validation + When I select the email marketing preference + And I enter a valid marketing preference email + And I clear the marketing preference email + Then I should see the marketing preference email error "Please fill in your email address" + When I enter an invalid marketing preference email "example@£$^&email.com" + Then I should see the marketing preference email error "Please fill in a valid email address" + When I enter a valid marketing preference email + And I submit the Giftaid form + Then I should see the Giftaid thank you message + + Scenario: Phone marketing preference field validation + When I select the phone marketing preference + And I enter a valid marketing preference phone + And I clear the marketing preference phone + Then I should see the marketing preference phone error "Please fill in your phone number" + When I enter an invalid marketing preference phone "0208569424" + Then I should see the marketing preference phone error "Please fill in a valid UK phone number, with no spaces" + When I enter a valid marketing preference phone + And I submit the Giftaid form + Then I should see the Giftaid thank you message diff --git a/playwright-staging/tests/features/submit/postcodeLookup.feature b/playwright-staging/tests/features/submit/postcodeLookup.feature new file mode 100644 index 00000000..949c9afc --- /dev/null +++ b/playwright-staging/tests/features/submit/postcodeLookup.feature @@ -0,0 +1,23 @@ +@sanity @postcode +Feature: Postcode validation + + Background: + Given I am on the Giftaid page + And I select the Giftaid option + And I enter the supporter details + + Scenario Outline: Invalid postcode formatting should show error message + When I enter the postcode "" + Then I should see the postcode error message "" + + Examples: + | postcode | message | + | S E 1 7 T P | Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below. | + | SE$%TP | Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below. | + + Scenario: Valid UK postcode using postcode lookup should submit the form + When I enter the postcode "SE1 7TP" + And I search for the postcode + And I select the address from lookup or enter address manually + And I submit the Giftaid form + Then I should see the Giftaid thank you message with line break diff --git a/playwright-staging/tests/features/submit/sorry.feature b/playwright-staging/tests/features/submit/sorry.feature new file mode 100644 index 00000000..7dac52a6 --- /dev/null +++ b/playwright-staging/tests/features/submit/sorry.feature @@ -0,0 +1,7 @@ +@sanity @sorry +Feature: Giftaid sorry page + + Scenario: Accessing giftaid sorry page should show the sorry message + Given I navigate to the Giftaid sorry page + Then I should see the sorry heading + And I should see the sorry message paragraph diff --git a/playwright-staging/tests/features/submit/successRedirect.feature b/playwright-staging/tests/features/submit/successRedirect.feature new file mode 100644 index 00000000..a0a1398d --- /dev/null +++ b/playwright-staging/tests/features/submit/successRedirect.feature @@ -0,0 +1,10 @@ +@sanity @success-redirect +Feature: Giftaid success page redirect + + Scenario: Accessing success page should redirect to giftaid homepage + Given I navigate to the Giftaid success page + Then I should be redirected to the Giftaid homepage + And I should see the Giftaid heading + And I should see the Giftaid option + And I should see the mobile input field + diff --git a/playwright-staging/tests/features/submit/validGifaidSubmission.feature b/playwright-staging/tests/features/submit/validGifaidSubmission.feature new file mode 100644 index 00000000..bd19a197 --- /dev/null +++ b/playwright-staging/tests/features/submit/validGifaidSubmission.feature @@ -0,0 +1,11 @@ +@sanity @valid-submission +Feature: Valid Giftaid Submission + + Scenario: Valid Giftaid Submission + Given I am on the Giftaid page + And I select the Giftaid option + And I complete the Giftaid form with valid details + And I select the marketing preferences + When I submit the Giftaid form + Then I should see the Giftaid thank you message + diff --git a/playwright-staging/tests/features/update/formValidation.feature b/playwright-staging/tests/features/update/formValidation.feature new file mode 100644 index 00000000..d13d43c3 --- /dev/null +++ b/playwright-staging/tests/features/update/formValidation.feature @@ -0,0 +1,87 @@ +@sanity @update-form-validation +Feature: Giftaid update form validation + + Background: + Given I am on the Giftaid update page + + Scenario: Empty input fields should show error messages + When I submit the Giftaid update form + Then I should see the required update form error messages + + Scenario Outline: Invalid first name values should show error message on update form + When I enter the update first name "" + Then I should see the update first name error message "" + + Examples: + | firstName | message | + | Test^$%£ | This field only accepts alphabetic characters and ' - | + | SPACE | This field only accepts alphabetic characters and ' - | + | 123Test | This field only accepts alphabetic characters and ' - | + + Scenario: Valid first name should submit update form + When I complete the Giftaid update form with first name "John" + And I select yes for GiftAid declaration + And I submit the Giftaid update form + Then I should see the update thank you message for "John" + + Scenario Outline: Invalid email values should show error message on update form + When I enter the update email "" + Then I should see the update email error message "" + + Examples: + | email | message | + | test@comic$relief.com | Please fill in a valid email address | + | test@c{(micrelief.com | Please fill in a valid email address | + | test@comic%relief.com | Please fill in a valid email address | + | Test0-9!#$%&'*+/=?^_{\|}~-@comicrelief_9-8.com.uk | Please fill in a valid email address | + + Scenario: Valid email should submit update form with no declaration + When I complete the Giftaid update form with the email + And I select no for GiftAid declaration + And I submit the Giftaid update form + Then I should see the update no declaration message + + Scenario Outline: Invalid mobile numbers should show error message on update form + When I enter the update mobile number "" + Then I should see the update mobile error message "" + + Examples: + | mobile | message | + | 0722345678 | Please enter a valid mobile phone number - it must be the same number associated with your donation. | + | 0722345678900 | Please enter a valid mobile phone number - it must be the same number associated with your donation. | + | 0722 345 6789 | Please enter a valid mobile phone number - it must be the same number associated with your donation. | + | 0780ab5694245 | Please enter a valid mobile phone number - it must be the same number associated with your donation. | + + Scenario Outline: Valid mobile numbers should not show error message on update form + When I enter the update mobile number "" + Then I should not see the update mobile error message + + Examples: + | mobile | + | 07123456789 | + | 07340707252 | + + Scenario: Valid mobile number should submit update form + When I complete the Giftaid update form with the mobile and last name "test" + And I select yes for GiftAid declaration + And I submit the Giftaid update form + Then I should see the update thank you message for " test" + + Scenario Outline: Invalid postcode values should show error message on update form + When I enter the update postcode "" + Then I should see the update postcode error message "" + + Examples: + | postcode | message | + | S E 1 7 T P | Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below. | + | SE$%TP | Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below. | + | cro 7tp | Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below. | + + Scenario: Valid postcode should submit update form + When I enter the update postcode "SE1 7TP" + And I search for the update postcode + And I select the update address from lookup or enter address manually + And I complete the remaining update form fields + And I select yes for GiftAid declaration + And I submit the Giftaid update form + Then I should see the update thank you message for " test" diff --git a/playwright-staging/tests/features/update/giftaidDeclarationOptions.feature b/playwright-staging/tests/features/update/giftaidDeclarationOptions.feature new file mode 100644 index 00000000..30012122 --- /dev/null +++ b/playwright-staging/tests/features/update/giftaidDeclarationOptions.feature @@ -0,0 +1,17 @@ +@sanity @update-declaration +Feature: Giftaid declaration claim selection + + Scenario: Validate Giftaid declaration claim selections + Given I am on the Giftaid update page + When I complete the Giftaid update form with valid details + And I submit the Giftaid update form + Then I should see the Giftaid declaration section + + When I select yes for GiftAid declaration + Then yes option should be selected and no option should not be selected + + When I select no for GiftAid declaration + Then no option should be selected and yes option should not be selected + + When I submit the Giftaid update form + Then I should see the update no declaration message diff --git a/playwright-staging/tests/features/update/internationalAddressesValidation.feature b/playwright-staging/tests/features/update/internationalAddressesValidation.feature new file mode 100644 index 00000000..de710a6b --- /dev/null +++ b/playwright-staging/tests/features/update/internationalAddressesValidation.feature @@ -0,0 +1,14 @@ +@sanity @update-international-address +Feature: International address validation on update form + + Scenario: Selecting a non-UK country and entering a non-UK postcode should submit the update form + Given I am on the Giftaid update page + And I enter the update supporter details + When I enter the update postcode "30916-395" + Then I should see the update postcode error message "Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below." + When I enter the update international address details manually + And I select a non UK country on the update form + Then the update postcode error should disappear + When I select yes for GiftAid declaration + And I submit the Giftaid update form + Then I should see the update thank you message for "test" diff --git a/playwright-staging/tests/features/update/updateSorry.feature b/playwright-staging/tests/features/update/updateSorry.feature new file mode 100644 index 00000000..23864263 --- /dev/null +++ b/playwright-staging/tests/features/update/updateSorry.feature @@ -0,0 +1,7 @@ +@sanity @update-sorry +Feature: Giftaid update sorry page + + Scenario: Accessing giftaid update sorry page should show the sorry message + Given I navigate to the Giftaid update sorry page + Then I should see the update sorry heading + And I should see the update sorry message diff --git a/playwright-staging/tests/features/update/updateSuccessRedirect.feature b/playwright-staging/tests/features/update/updateSuccessRedirect.feature new file mode 100644 index 00000000..0b04b0ce --- /dev/null +++ b/playwright-staging/tests/features/update/updateSuccessRedirect.feature @@ -0,0 +1,7 @@ +@sanity @update-success-redirect +Feature: Giftaid update success page redirect + + Scenario: Accessing success page should redirect to giftaid update homepage + Given I navigate to the Giftaid update success page + Then I should be redirected to the Giftaid homepage + diff --git a/playwright-staging/tests/features/update/validFormSubmission.feature b/playwright-staging/tests/features/update/validFormSubmission.feature new file mode 100644 index 00000000..c0210323 --- /dev/null +++ b/playwright-staging/tests/features/update/validFormSubmission.feature @@ -0,0 +1,11 @@ +@sanity @update-valid-submission +Feature: Valid Giftaid update submission + + # Step definitions for this feature are implemented in: + # tests/step-definitions/update/giftaidUpdateCommon.steps.js + Scenario: Valid giftaid update submission + Given I am on the Giftaid update page + When I complete the Giftaid update form with valid details + And I select yes for GiftAid declaration + And I submit the Giftaid update form + Then I should see the update thank you message diff --git a/playwright-staging/tests/step-definitions/common/giftaidCommon.steps.js b/playwright-staging/tests/step-definitions/common/giftaidCommon.steps.js new file mode 100644 index 00000000..9262f05f --- /dev/null +++ b/playwright-staging/tests/step-definitions/common/giftaidCommon.steps.js @@ -0,0 +1,53 @@ +const { When, Then, Given} = require('@cucumber/cucumber'); +const { expect } = require('@playwright/test'); +const Chance = require('chance'); +const { selectors } = require('../../utils/locators'); + +const chance = new Chance(); + +Given('I am on the Giftaid page', async function () { + await this.page.goto(process.env.BASE_URL, { timeout: 30000 }); + await this.page.waitForLoadState('domcontentloaded'); +}); + +When('I select the Giftaid option', async function () { + await this.page.click(selectors.giftaid.option); +}); + +Then('I should see the Giftaid thank you message', async function () { + const expectedFirstName = this.supporter?.firstName || 'test'; + await expect(this.page.locator('h1')).toContainText( + `Thank you, ${expectedFirstName}!`, + { timeout: 30000 } + ); +}); + +When('I submit the Giftaid form', async function () { + await this.page.locator(selectors.formFields.submitButton).click(); +}); + +When('I select the marketing preferences', async function () { + await this.commands.selectMarketingPrefs({ + email: this.supporter?.email, + phone: this.supporter?.phone, + }); +}); + +When('I complete the Giftaid form with valid details', async function () { + this.supporter = { + firstName: chance.first(), + lastName: chance.last(), + mobile: chance.phone({ country: 'uk', mobile: true }).replace(/\s/g, ''), + postcode: chance.postcode(), + address1: chance.address(), + address2: chance.street(), + address3: 'test address 3', + town: chance.city(), + }; + + await this.commands.populateFormFields(this.supporter); +}); + +Then('I should be redirected to the Giftaid homepage', async function () { + await expect(this.page.locator(selectors.homepage.heading)).toContainText('Giftaid it'); +}); diff --git a/playwright-staging/tests/step-definitions/submit/addressValidation.steps.js b/playwright-staging/tests/step-definitions/submit/addressValidation.steps.js new file mode 100644 index 00000000..dd70daf8 --- /dev/null +++ b/playwright-staging/tests/step-definitions/submit/addressValidation.steps.js @@ -0,0 +1,50 @@ +const { When, Then } = require('@cucumber/cucumber'); +const { expect } = require('@playwright/test'); +const { selectors } = require('../../utils/locators'); + +When('I clear the postcode field', async function () { + const postcodeField = this.page.locator(selectors.formFields.postcode); + await postcodeField.fill(''); + await expect(postcodeField).toHaveValue(''); +}); + +Then('I should see the address dropdown', async function () { + await expect(this.page.locator(selectors.address.addressSelect)).toBeVisible(); +}); + +Then('I should see the address select error message {string}', async function (message) { + await expect(this.page.locator(selectors.errorMessages.addressSelect)).toHaveText(message); +}); + +Then('I should see the manual address link', async function () { + await expect(this.page.locator(selectors.address.manualAddressLink)).toBeVisible(); +}); + +When('I click the manual address link', async function () { + await this.page.locator(selectors.address.manualAddressLink).click(); +}); + +Then('I should see the manual address fields', async function () { + await expect(this.page.locator(selectors.address.address1)).toBeVisible(); + await expect(this.page.locator(selectors.address.address2)).toBeVisible(); + await expect(this.page.locator(selectors.address.address3)).toBeVisible(); + await expect(this.page.locator(selectors.address.town)).toBeVisible(); + await expect(this.page.locator(selectors.address.country)).toBeVisible(); +}); + +When('I enter an invalid address line 1', async function () { + // Should see error message for address1 when input with special characters is entered + await this.page.locator(selectors.address.address1).fill('@£%3dComic Relief'); +}); + +Then('I should see the address line 1 error message', async function () { + await expect(this.page.locator(selectors.errorMessages.address1)).toHaveText("This field only accepts alphanumeric characters and ' . - & _ /"); +}); + +When('I enter an invalid town', async function () { + await this.page.locator(selectors.address.town).fill(' Comic Relief'); +}); + +Then('I should see the town error message', async function () { + await expect(this.page.locator(selectors.errorMessages.town)).toHaveText("This field only accepts alphanumeric characters and ' . - & _ /"); +}); diff --git a/playwright-staging/tests/step-definitions/submit/formValidation.steps.js b/playwright-staging/tests/step-definitions/submit/formValidation.steps.js new file mode 100644 index 00000000..06aa2fc8 --- /dev/null +++ b/playwright-staging/tests/step-definitions/submit/formValidation.steps.js @@ -0,0 +1,48 @@ +const { When, Then } = require('@cucumber/cucumber'); +const { expect } = require('@playwright/test'); +const { selectors } = require('../../utils/locators'); + +When('I enter the mobile number {string}', async function (mobile) { + await this.page.locator(selectors.formFields.mobile).fill(''); + await this.page.locator(selectors.formFields.mobile).type(mobile, { delay: 100 }); +}); + +Then('I should see the mobile error message {string}', async function (message) { + await expect(this.page.locator(selectors.errorMessages.mobile)).toHaveText(message); +}); + +When('I complete the Giftaid form with the mobile number {string}', async function (mobile) { + await this.page.locator(selectors.formFields.mobile).fill(''); + await this.commands.populateFormFields({ mobile }); +}); + +When('I enter the first name {string}', async function (firstName) { + const value = firstName === 'SPACE' ? ' ' : firstName; + await this.page.locator(selectors.formFields.firstName).fill(value); +}); + +Then('I should see the first name error message {string}', async function (message) { + await expect(this.page.locator(selectors.errorMessages.firstName)).toHaveText(message); +}); + +When('I complete the Giftaid form with the first name {string}', async function (firstName) { + await this.page.locator(selectors.formFields.firstName).fill(''); + await this.commands.populateFormFields({ firstName }); +}); + +Then('I should see the thank you message for {string}', async function (firstName) { + await expect(this.page.locator(selectors.success.heading)).toHaveText(`Thank you, ${firstName}!`); +}); + +When('I enter the last name {string}', async function (lastName) { + const value = lastName === 'SPACE' ? ' ' : lastName; + await this.page.locator(selectors.formFields.lastName).fill(value); +}); + +Then('I should see the last name error message {string}', async function (message) { + await expect(this.page.locator(selectors.errorMessages.lastName)).toHaveText(message); +}); + +Then('I should not see the last name error message', async function () { + expect(await this.page.locator(selectors.errorMessages.lastName).count()).toEqual(0); +}); diff --git a/playwright-staging/tests/step-definitions/submit/internationalAddressValidation.steps.js b/playwright-staging/tests/step-definitions/submit/internationalAddressValidation.steps.js new file mode 100644 index 00000000..83f790a9 --- /dev/null +++ b/playwright-staging/tests/step-definitions/submit/internationalAddressValidation.steps.js @@ -0,0 +1,35 @@ +const { When, Then } = require('@cucumber/cucumber'); +const { expect } = require('@playwright/test'); +const { selectors } = require('../../utils/locators'); + +When('I enter a non UK postcode', async function () { + await this.page.fill(selectors.formFields.postcode, '30916-395'); +}); + +Then('I should see the postcode validation error for UK format', async function () { + await expect(this.page.locator(selectors.errorMessages.postcode)).toContainText( + 'Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below.' + ); +}); + +When('I enter the international address details manually', async function () { + await this.page.click(selectors.address.manualAddressLink); + await this.page.fill(selectors.address.address1, '219 Beacon St'); + await this.page.fill(selectors.address.address2, 'Winder'); + await this.page.fill(selectors.address.town, 'GA'); +}); + +When('I select a non UK country', async function () { + const countries = await this.page.$$eval(selectors.address.countryOptions, options => + options + .map(option => option.value) + .filter(value => value && value !== 'GB') + ); + + const randomCountryCode = countries[Math.floor(Math.random() * countries.length)]; + await this.page.selectOption(selectors.address.countryByName, { value: randomCountryCode }); +}); + +Then('the postcode error should disappear', async function () { + await expect(this.page.locator(selectors.errorMessages.postcode)).not.toBeVisible(); +}); diff --git a/playwright-staging/tests/step-definitions/submit/marketingPreferencesData.steps.js b/playwright-staging/tests/step-definitions/submit/marketingPreferencesData.steps.js new file mode 100644 index 00000000..c2b4496e --- /dev/null +++ b/playwright-staging/tests/step-definitions/submit/marketingPreferencesData.steps.js @@ -0,0 +1,61 @@ +const { When, Then } = require('@cucumber/cucumber'); +const { expect } = require('@playwright/test'); +const Chance = require('chance'); +const { MarketingPrefsVerify } = require('../../utils/marketingPrefsVerify'); + +const chance = new Chance(); + +When('I populate the Giftaid form with the supporter details', async function () { + this.supporter = { + firstName: chance.first(), + lastName: chance.last(), + phone: chance.phone({ country: 'uk', mobile: false }).replace(/\s/g, ''), // UK phone number + mobile: chance.phone({ country: 'uk', mobile: true }).replace(/\s/g, ''), // UK mobile number + address1: chance.street(), + address2: chance.street(), + address3: chance.city(), + postcode: 'SE1 7TP', + town: chance.county(), + email: `giftaid-staging-${Date.now().toString()}@email.sls.comicrelief.com`, + }; + + // Populate all input fields using Commands class + await this.commands.populateFormFields({ + mobile: this.supporter.mobile, + firstName: this.supporter.firstName, + lastName: this.supporter.lastName, + address1: this.supporter.address1, + address2: this.supporter.address2, + address3: this.supporter.address3, + town: this.supporter.town, + postcode: this.supporter.postcode, + }); +}); + +Then('I should see the supporter thank you message', async function () { + await expect(this.page.locator('h1')).toHaveText( + `Thank you, ${this.supporter.firstName}!`, + { timeout: 15000 } + ); +}); + +Then('the marketing preferences data should be stored in the contact-store', async function () { + // Retrieve and verify marketing preferences data + const mpData = await MarketingPrefsVerify.get(this.supporter.email); + + expect(mpData.campaign).toEqual('SR26'); + expect(mpData.firstname).toEqual(this.supporter.firstName); + expect(mpData.lastname).toEqual(this.supporter.lastName); + expect(mpData.email).toEqual(this.supporter.email); + expect(mpData.address1).toEqual(this.supporter.address1); + expect(mpData.town).toEqual(this.supporter.town); + expect(mpData.country).toEqual('GB'); + expect(mpData.transsourceurl).toContain(process.env.BASE_URL); + expect(mpData.transsource).toEqual('SR26_GiftAid'); + expect(mpData.transtype).toEqual('prefs'); + expect(mpData.permissionemail).toEqual('1'); + expect(mpData.permissionsms).toEqual('1'); + expect(mpData.permissionphone).toEqual('1'); + expect(mpData.phone).toEqual(this.supporter.phone); + expect(mpData.mobile).toEqual(this.supporter.mobile.replace(/^[07]{1}/, '+44')); +}); diff --git a/playwright-staging/tests/step-definitions/submit/marketingPreferencesValidation.steps.js b/playwright-staging/tests/step-definitions/submit/marketingPreferencesValidation.steps.js new file mode 100644 index 00000000..61cf9645 --- /dev/null +++ b/playwright-staging/tests/step-definitions/submit/marketingPreferencesValidation.steps.js @@ -0,0 +1,70 @@ +const { When, Then } = require('@cucumber/cucumber'); +const { expect } = require('@playwright/test'); +const Chance = require('chance'); +const { selectors } = require('../../utils/locators'); + +const chance = new Chance(); + +const email = `giftaid-update-staging-${chance.email()}`; +const phone = chance.phone({ country: 'uk', mobile: false }).replace(/\s/g, ''); + +When('I select all the marketing preference options', async function () { + const marketingOptions = [ + selectors.marketingPreferences.options.email, + selectors.marketingPreferences.options.phone, + selectors.marketingPreferences.options.text, + ]; + + for (const option of marketingOptions) { + await this.page.click(option); + expect(await this.page.locator(option).isChecked()).toBeTruthy(); + } +}); + +When('I enter the valid marketing preference contact details', async function () { + await expect(this.page.locator(selectors.marketingPreferences.fields.email)).toBeVisible(); + await this.page.fill(selectors.marketingPreferences.fields.email, email); + + await expect(this.page.locator(selectors.marketingPreferences.fields.phone)).toBeVisible(); + await this.page.fill(selectors.marketingPreferences.fields.phone, phone); +}); + +When('I select the email marketing preference', async function () { + await this.page.click(selectors.marketingPreferences.options.email); +}); + +When('I enter a valid marketing preference email', async function () { + await this.page.fill(selectors.marketingPreferences.fields.email, email); +}); + +When('I clear the marketing preference email', async function () { + await this.page.fill(selectors.marketingPreferences.fields.email, ''); +}); + +When('I enter an invalid marketing preference email {string}', async function (invalidEmail) { + await this.page.fill(selectors.marketingPreferences.fields.email, invalidEmail); +}); + +Then('I should see the marketing preference email error {string}', async function (message) { + await expect(this.page.locator(selectors.errorMessages.email)).toHaveText(message); +}); + +When('I select the phone marketing preference', async function () { + await this.page.click(selectors.marketingPreferences.options.phone); +}); + +When('I enter a valid marketing preference phone', async function () { + await this.page.fill(selectors.marketingPreferences.fields.phone, phone); +}); + +When('I clear the marketing preference phone', async function () { + await this.page.fill(selectors.marketingPreferences.fields.phone, ''); +}); + +When('I enter an invalid marketing preference phone {string}', async function (invalidPhone) { + await this.page.fill(selectors.marketingPreferences.fields.phone, invalidPhone); +}); + +Then('I should see the marketing preference phone error {string}', async function (message) { + await expect(this.page.locator(selectors.errorMessages.phone)).toHaveText(message); +}); diff --git a/playwright-staging/tests/step-definitions/submit/postcodeValidation.steps.js b/playwright-staging/tests/step-definitions/submit/postcodeValidation.steps.js new file mode 100644 index 00000000..8c03b79c --- /dev/null +++ b/playwright-staging/tests/step-definitions/submit/postcodeValidation.steps.js @@ -0,0 +1,44 @@ +const { Given, When, Then } = require('@cucumber/cucumber'); +const { expect } = require('@playwright/test'); +const { selectors } = require('../../utils/locators'); + +Given('I enter the supporter details', async function () { + await this.page.fill(selectors.formFields.mobile, '07123456789'); + await this.page.fill(selectors.formFields.firstName, 'test'); + await this.page.fill(selectors.formFields.lastName, 'user'); +}); + +When('I enter the postcode {string}', async function (postcode) { + const postcodeField = this.page.locator(selectors.formFields.postcode); + + await postcodeField.fill(''); + await postcodeField.fill(postcode); + + await expect(postcodeField).toHaveValue(postcode); +}); + +Then('I should see the postcode error message {string}', async function (message) { + await expect(this.page.locator(selectors.errorMessages.postcode)).toBeVisible(); + await expect(this.page.locator(selectors.errorMessages.postcode)).toContainText(message); +}); + +When('I search for the postcode', async function () { + await this.page.click(selectors.formFields.postcodeLookup); +}); + +When('I select the address from lookup or enter address manually', async function () { + if (await this.page.isVisible(selectors.address.addressSelect)) { + await this.page.selectOption(selectors.address.addressSelect, { label: 'COMIC RELIEF, CAMELFORD HOUSE 87-90' }); + await expect(this.page.locator(selectors.formFields.postcode)).toHaveValue('SE1 7TP'); + } else { + await this.page.click(selectors.address.manualAddressLink); + await this.page.fill(selectors.address.address1, 'COMIC RELIEF'); + await this.page.fill(selectors.address.address2, 'CAMELFORD HOUSE 87-90'); + await this.page.fill(selectors.address.address3, 'ALBERT EMBANKMENT'); + await this.page.fill(selectors.address.town, 'LONDON'); + } +}); + +Then('I should see the Giftaid thank you message with line break', async function () { + await expect(this.page.locator(selectors.success.heading)).toContainText('Thank you,\n' + 'test!'); +}); diff --git a/playwright-staging/tests/step-definitions/submit/sorryPage.steps.js b/playwright-staging/tests/step-definitions/submit/sorryPage.steps.js new file mode 100644 index 00000000..be723d9c --- /dev/null +++ b/playwright-staging/tests/step-definitions/submit/sorryPage.steps.js @@ -0,0 +1,18 @@ +const { Given, Then } = require('@cucumber/cucumber'); +const { expect } = require('@playwright/test'); +const { selectors } = require('../../utils/locators'); + +Given('I navigate to the Giftaid sorry page', async function () { + await this.page.goto(`${process.env.BASE_URL}sorry`, { timeout: 30000 }); +}); + +Then('I should see the sorry heading', async function () { + const header = this.page.locator(selectors.sorry.heading); + await expect(header).toBeVisible(); + await expect(header).toContainText('Sorry!'); +}); + +Then('I should see the sorry message paragraph', async function () { + const firstParagraph = this.page.locator(selectors.sorry.firstParagraph); + await expect(firstParagraph).toBeVisible(); +}); diff --git a/playwright-staging/tests/step-definitions/submit/successRedirect.steps.js b/playwright-staging/tests/step-definitions/submit/successRedirect.steps.js new file mode 100644 index 00000000..c297bfbf --- /dev/null +++ b/playwright-staging/tests/step-definitions/submit/successRedirect.steps.js @@ -0,0 +1,20 @@ +const { Given, Then } = require('@cucumber/cucumber'); +const { expect } = require('@playwright/test'); +const { selectors } = require('../../utils/locators'); + +Given('I navigate to the Giftaid success page', async function () { + await this.page.goto(`${process.env.BASE_URL}success`, { timeout: 30000 }); + await this.page.waitForLoadState('domcontentloaded'); +}); + +Then('I should see the Giftaid heading', async function () { + await expect(this.page.locator(selectors.homepage.heading)).toBeVisible(); +}); + +Then('I should see the Giftaid option', async function () { + await expect(this.page.locator(selectors.giftaid.option)).toBeVisible(); +}); + +Then('I should see the mobile input field', async function () { + await expect(this.page.locator(selectors.formFields.mobile)).toBeVisible(); +}); diff --git a/playwright-staging/tests/step-definitions/update/giftaidDeclaration.steps.js b/playwright-staging/tests/step-definitions/update/giftaidDeclaration.steps.js new file mode 100644 index 00000000..a55e8b98 --- /dev/null +++ b/playwright-staging/tests/step-definitions/update/giftaidDeclaration.steps.js @@ -0,0 +1,18 @@ +const { Then } = require('@cucumber/cucumber'); +const { expect } = require('@playwright/test'); +const { selectors } = require('../../utils/locators'); + +Then('I should see the Giftaid declaration section', async function () { + await expect(this.page.locator(selectors.giftAidClaimChoice.yes)).toBeVisible(); + await expect(this.page.locator(selectors.giftAidClaimChoice.no)).toBeVisible(); +}); + +Then('yes option should be selected and no option should not be selected', async function () { + await expect(this.page.locator(selectors.giftAidClaimChoice.yesInput)).toBeChecked(); + await expect(this.page.locator(selectors.giftAidClaimChoice.noInput)).not.toBeChecked(); +}); + +Then('no option should be selected and yes option should not be selected', async function () { + await expect(this.page.locator(selectors.giftAidClaimChoice.noInput)).toBeChecked(); + await expect(this.page.locator(selectors.giftAidClaimChoice.yesInput)).not.toBeChecked(); +}); diff --git a/playwright-staging/tests/step-definitions/update/giftaidUpdateCommon.steps.js b/playwright-staging/tests/step-definitions/update/giftaidUpdateCommon.steps.js new file mode 100644 index 00000000..4707f923 --- /dev/null +++ b/playwright-staging/tests/step-definitions/update/giftaidUpdateCommon.steps.js @@ -0,0 +1,46 @@ +const { Given, When, Then } = require('@cucumber/cucumber'); +const { expect } = require('@playwright/test'); +const { selectors } = require('../../utils/locators'); + +Given('I am on the Giftaid update page', async function () { + await this.page.goto(`${process.env.BASE_URL}update`, { timeout: 30000 }); + await this.page.waitForLoadState('domcontentloaded'); +}); + +When('I complete the Giftaid update form with valid details', async function () { + await this.commands.populateUpdateFormFields(); +}); + +When('I submit the Giftaid update form', async function () { + await this.page.click(selectors.formFields.submitButton); +}); + +When('I select yes for GiftAid declaration', async function () { + await this.page.click(selectors.giftAidClaimChoice.yes); +}); + +When('I select no for GiftAid declaration', async function () { + await this.page.click(selectors.giftAidClaimChoice.no); +}); + +When('I enter the update postcode {string}', async function (postcode) { + await this.page.fill(selectors.formFields.postcode, ''); + await this.page.type(selectors.formFields.postcode, postcode); +}); + +Then('I should see the update postcode error message {string}', async function (message) { + await expect(this.page.locator(selectors.errorMessages.postcode)).toBeVisible(); + await expect(this.page.locator(selectors.errorMessages.postcode)).toHaveText(message); +}); + +Then('I should see the update thank you message for {string}', async function (firstName) { + await expect(this.page.locator(selectors.success.heading)).toHaveText(`Thank you, ${firstName}!`); +}); + +Then('I should see the update no declaration message', async function () { + await expect(this.page.locator(selectors.success.heading)).toHaveText('Thanks for letting us know'); +}); + +Then('I should see the update thank you message', async function () { + await expect(this.page.locator(selectors.success.heading)).toContainText('Thank you, test!'); +}); diff --git a/playwright-staging/tests/step-definitions/update/internationalAddressValidation.steps.js b/playwright-staging/tests/step-definitions/update/internationalAddressValidation.steps.js new file mode 100644 index 00000000..1efc89d7 --- /dev/null +++ b/playwright-staging/tests/step-definitions/update/internationalAddressValidation.steps.js @@ -0,0 +1,38 @@ +const { Given, When, Then } = require('@cucumber/cucumber'); +const { expect } = require('@playwright/test'); +const { selectors } = require('../../utils/locators'); + +Given('I enter the update supporter details', async function () { + // fill in all input fields + // await page.locator('input#field-input--transactionId').fill(transactionId); + await this.page.locator(selectors.formFields.firstName).fill('test'); + await this.page.locator(selectors.formFields.lastName).fill('test lastname'); + await this.page.locator(selectors.marketingPreferences.fields.email).fill('giftaid-staging-@email.sls.comicrelief.com'); +}); + +When('I enter the update international address details manually', async function () { + // manually enter international address details + await this.page.locator(selectors.address.manualAddressLink).click(); + await this.page.locator(selectors.address.address1).fill('219 Beacon St'); + await this.page.locator(selectors.address.address2).fill('Winder'); + await this.page.locator(selectors.address.address3).fill('Park Ridge'); + await this.page.locator(selectors.address.town).fill('GA'); +}); + +When('I select a non UK country on the update form', async function () { + // Select a random country from the dropdown (excluding UK to simulate international address) + const countryOptions = await this.page.$$eval(selectors.address.countryOptions, options => + options.map(option => option.value).filter(value => value && value !== 'GB') + ); + + const randomCountryCode = countryOptions[Math.floor(Math.random() * countryOptions.length)]; + await this.page.locator(selectors.address.countryByName).selectOption({ value: randomCountryCode }); + + // Wait for the form to adjust to the selected country + await this.page.waitForTimeout(2000); +}); + +Then('the update postcode error should disappear', async function () { + // When an international country is selected, the postcode error for UK format should not show anymore + await expect(this.page.locator(selectors.errorMessages.postcode)).not.toBeVisible(); +}); diff --git a/playwright-staging/tests/step-definitions/update/sorryPage.steps.js b/playwright-staging/tests/step-definitions/update/sorryPage.steps.js new file mode 100644 index 00000000..0c5691f1 --- /dev/null +++ b/playwright-staging/tests/step-definitions/update/sorryPage.steps.js @@ -0,0 +1,18 @@ +const { Given, Then } = require('@cucumber/cucumber'); +const { expect } = require('@playwright/test'); +const { selectors } = require('../../utils/locators'); + +Given('I navigate to the Giftaid update sorry page', async function () { + // Navigate to the 'Sorry' page of giftaid update form + await this.page.goto(`${process.env.BASE_URL}update/sorry`, { timeout: 30000 }); + await this.page.waitForLoadState('domcontentloaded'); +}); + +Then('I should see the update sorry heading', async function () { + await expect(this.page.locator(selectors.sorry.heading)).toContainText('Sorry!'); +}); + +Then('I should see the update sorry message', async function () { + const sorryMessage = await this.page.locator(selectors.sorry.firstParagraph).isVisible(); + expect(sorryMessage).toBeTruthy(); +}); diff --git a/playwright-staging/tests/step-definitions/update/updateFormValidation.steps.js b/playwright-staging/tests/step-definitions/update/updateFormValidation.steps.js new file mode 100644 index 00000000..df89b4b5 --- /dev/null +++ b/playwright-staging/tests/step-definitions/update/updateFormValidation.steps.js @@ -0,0 +1,103 @@ +const { When, Then } = require('@cucumber/cucumber'); +const { expect } = require('@playwright/test'); +const Chance = require('chance'); +const { selectors } = require('../../utils/locators'); + +const chance = new Chance(); + +Then('I should see the required update form error messages', async function () { + await expect(this.page.locator(selectors.errorMessages.firstName)).toHaveText('Please fill in your first name'); + await expect(this.page.locator(selectors.errorMessages.lastName)).toHaveText('Please fill in your last name'); + await expect(this.page.locator(selectors.errorMessages.email)).toHaveText('Please fill in your email address'); + await expect(this.page.locator(selectors.errorMessages.postcode)).toHaveText('Please enter your postcode'); + await expect(this.page.locator(selectors.errorMessages.addressDetails)).toHaveText('Please fill in your address'); + await expect(this.page.locator(selectors.errorMessages.giftAidClaimChoice)).toHaveText('This field is required'); +}); + +When('I enter the update first name {string}', async function (firstName) { + const value = firstName === 'SPACE' ? ' ' : firstName; + await this.page.fill(selectors.formFields.firstName, value); + await this.page.keyboard.press('Enter'); +}); + +Then('I should see the update first name error message {string}', async function (message) { + await expect(this.page.locator(selectors.errorMessages.firstName)).toHaveText(message); +}); + +When('I complete the Giftaid update form with first name {string}', async function (firstName) { + await this.page.fill(selectors.formFields.firstName, ''); + await this.commands.populateUpdateFormFields({ firstName }); +}); + +When('I select yes for the GiftAid declaration', async function () { + await this.page.click(selectors.giftAidClaimChoice.yes); +}); + +When('I select no for the GiftAid declaration', async function () { + await this.page.click(selectors.giftAidClaimChoice.no); +}); + +When('I enter the update email {string}', async function (email) { + await this.page.fill(selectors.marketingPreferences.fields.email, ''); + await this.page.fill(selectors.marketingPreferences.fields.email, email); + await this.page.keyboard.press('Enter'); +}); + +Then('I should see the update email error message {string}', async function (message) { + await expect(this.page.locator(selectors.errorMessages.email)).toBeVisible(); + await expect(this.page.locator(selectors.errorMessages.email)).toHaveText(message); +}); + +When('I complete the Giftaid update form with the email', async function () { + const validEmail = `giftaid-update-staging-${chance.email()}`; + await this.page.fill(selectors.marketingPreferences.fields.email, ''); + await this.commands.populateUpdateFormFields({ email: validEmail }); +}); + +When('I enter the update mobile number {string}', async function (mobile) { + await this.page.locator(selectors.formFields.mobile).fill(''); + await this.page.locator(selectors.formFields.mobile).type(mobile, { delay: 100 }); +}); + +Then('I should see the update mobile error message {string}', async function (message) { + await expect(this.page.locator(selectors.errorMessages.mobile)).toHaveText(message); +}); + +Then('I should not see the update mobile error message', async function () { + await expect(this.page.locator(selectors.errorMessages.mobile)).not.toBeVisible(); +}); + +When('I complete the Giftaid update form with the mobile and last name {string}', async function (lastName) { + const prefixes = ['071', '073', '074', '075', '077', '078', '079']; + const prefix = chance.pickone(prefixes); + const mobile = `${prefix}${chance.string({ pool: '0123456789', length: 8 })}`; + + await this.page.locator(selectors.formFields.mobile).fill(''); + await this.commands.populateUpdateFormFields({ lastName, mobile }); +}); + +When('I search for the update postcode', async function () { + await this.page.click(selectors.formFields.postcodeLookup); +}); + +When('I select the update address from lookup or enter address manually', async function () { + if (await this.page.locator(selectors.address.addressSelect).isVisible()) { + const options = await this.page.$$eval(selectors.address.addressSelectOptions, options => options.map(option => option.value)); + await this.page.selectOption(selectors.address.addressSelect, options[1]); + await this.page.click(selectors.formFields.submitButton); + } else { + await this.page.click(selectors.address.manualAddressLink); + await this.page.fill(selectors.address.address1, 'COMIC RELIEF'); + await this.page.fill(selectors.address.address2, 'CAMELFORD HOUSE 87-90'); + await this.page.fill(selectors.address.address3, 'ALBERT EMBANKMENT'); + await this.page.fill(selectors.address.town, 'LONDON'); + await this.page.click(selectors.formFields.submitButton); + } +}); + +When('I complete the remaining update form fields', async function () { + await this.page.locator(selectors.formFields.firstName).fill('test'); + await this.page.locator(selectors.formFields.lastName).fill(chance.last()); + await this.page.locator(selectors.marketingPreferences.fields.email).fill(`giftaid-update-staging-${chance.email()}`); + await this.page.fill(selectors.formFields.postcode, 'SE1 7TP'); +}); diff --git a/playwright-staging/tests/step-definitions/update/updateSuccessRedirect.steps.js b/playwright-staging/tests/step-definitions/update/updateSuccessRedirect.steps.js new file mode 100644 index 00000000..a40ec447 --- /dev/null +++ b/playwright-staging/tests/step-definitions/update/updateSuccessRedirect.steps.js @@ -0,0 +1,7 @@ +const { Given } = require('@cucumber/cucumber'); + +Given('I navigate to the Giftaid update success page', async function () { + // Navigate to the success page of the Giftaid update directly + await this.page.goto(`${process.env.BASE_URL}update/success`, { timeout: 30000 }); + await this.page.waitForLoadState('domcontentloaded'); +}); diff --git a/playwright-staging/tests/submit/addressValidation.spec.js b/playwright-staging/tests/submit/addressValidation.spec.js deleted file mode 100644 index 1a574546..00000000 --- a/playwright-staging/tests/submit/addressValidation.spec.js +++ /dev/null @@ -1,67 +0,0 @@ -// @ts-check -const { expect } = require('@playwright/test'); -const { test } = require('../../browserstack'); - -test.describe('Address validation @sanity @nightly-sanity', () => { - - test.beforeEach(async ({ page }) => { - // Navigate to the giftaid page - await page.goto(process.env.BASE_URL, { timeout: 30000 }); - await page.waitForLoadState('domcontentloaded'); - await page.locator('#field-label--giftaid').click(); - await page.locator('#field-input--mobile').fill('07123456789'); - await page.locator('input#field-input--firstname').fill('test'); - await page.locator('input#field-input--lastname').fill('user'); - }); - - test('empty postcode should show error message', async ({ page }) => { - await page.locator('input#field-input--postcode').fill(''); - await page.locator('button[type=submit]').click(); - await expect(page.locator('div#field-error--postcode > span')).toHaveText('Please enter your postcode'); - await page.close(); - }); - - test('invalid postcodes should show error messages', async ({ page }) => { - await page.locator('input#field-input--postcode').fill('12SE17TP'); - await expect(page.locator('div#field-error--postcode > span')).toHaveText('Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below.'); - await page.locator('input#field-input--postcode').fill('comic relief'); - await expect(page.locator('div#field-error--postcode > span')).toHaveText('Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below.'); - await page.locator('input#field-input--postcode').fill('cro 7tp'); - await expect(page.locator('div#field-error--postcode > span')).toHaveText('Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below.'); - await page.close(); - }); - - test('enter postcode but submit without selecting address should show error message', async ({ page }) => { - await page.locator('input#field-input--postcode').fill('SE1 7TP'); - await page.locator('#postcode_button').click(); - await expect(page.locator('#field-select--addressSelect')).toBeVisible(); - await page.locator('button[type=submit]').click(); - await expect(page.locator('div#field-error--addressSelect > span')).toHaveText('Please select your address'); - await page.close(); - }); - - test('clicking on manual address link should show address fields', async ({ page }) => { - await page.locator('input#field-input--postcode').fill('SE1 7TP'); - await expect(page.locator('a[aria-describedby=field-error--addressDetails]')).toBeVisible(); - await page.locator('a[aria-describedby=field-error--addressDetails]').click(); - await expect(page.locator('#field-input--address1')).toBeVisible(); - await expect(page.locator('#field-input--address2')).toBeVisible(); - await expect(page.locator('#field-input--address3')).toBeVisible(); - await expect(page.locator('#field-input--town')).toBeVisible(); - await expect(page.locator('select#field-select--country')).toBeVisible(); - await page.close(); - }); - - test('validate address fields', async ({ page }) => { - await page.locator('input#field-input--postcode').fill('SE1 7TP'); - await page.locator('a[aria-describedby=field-error--addressDetails]').click(); - - // Should see error message for address1 when inout with special characters is entered - await page.locator('#field-input--address1').fill('@£%3dComic Relief'); - await expect(page.locator('#field-error--address1 > span')).toHaveText("This field only accepts alphanumeric characters and ' . - & _ /"); - await page.locator('#field-input--town').fill(' Comic Relief'); - await expect(page.locator('#field-error--town > span')).toHaveText("This field only accepts alphanumeric characters and ' . - & _ /"); - - await page.close(); - }); -}); diff --git a/playwright-staging/tests/submit/formValidation.spec.js b/playwright-staging/tests/submit/formValidation.spec.js deleted file mode 100644 index 636e110f..00000000 --- a/playwright-staging/tests/submit/formValidation.spec.js +++ /dev/null @@ -1,82 +0,0 @@ -// @ts-check -const { expect } = require('@playwright/test'); -const { test } = require('../../browserstack'); -const { Commands } = require('../utils/commands'); - -test.describe('Form validation @sanity @nightly-sanity', () => { - - test.beforeEach(async ({ page }) => { - // Navigate to the giftaid page - await page.goto(process.env.BASE_URL, { timeout: 30000 }); - await page.waitForLoadState('domcontentloaded'); - await page.locator('#field-label--giftaid').click(); - }); - - test('Validate mobile number field', async ({ page }) => { - const commands = new Commands(page); - - // Test cases for various mobile number validations - const mobileTestCases = [ - { input: '0712345678', error: 'Please enter a valid mobile phone number - it must be the same number associated with your donation.' }, - { input: '0712345678900', error: 'Please enter a valid mobile phone number - it must be the same number associated with your donation.' }, - { input: '0712 345 6789', error: 'Please enter a valid mobile phone number - it must be the same number associated with your donation.' }, - { input: '0780ab5694245', error: 'Please enter a valid mobile phone number - it must be the same number associated with your donation.' }, - ]; - - for (let testCase of mobileTestCases) { - await page.locator('#field-input--mobile').fill(''); // Clear the field before each test - await page.locator('#field-input--mobile').type(testCase.input, { delay: 100 }); - await expect(page.locator('div#field-error--mobile > span')).toHaveText(testCase.error); - } - - // Validate correct mobile number - await page.locator('#field-input--mobile').fill(''); // Ensure the field is cleared and filled with valid data - await commands.populateFormFields(page, { mobile: '07123456789' }); - await page.locator('button[type=submit]').click(); - await expect(page.locator('div > h1')).toHaveText('Thank you, test!'); - }); - - test('validate first name field', async ({ page }) => { - const commands = new Commands(page); - - // First name with invalid characters - await page.locator('#field-input--firstname').fill('Test^$%£'); - await expect(page.locator('div#field-error--firstname')).toHaveText('This field only accepts 25 alphabetic characters and \' - starting with alphabetic characters'); - - // First name with just a space - await page.locator('#field-input--firstname').fill(' '); - await expect(page.locator('div#field-error--firstname')).toHaveText('This field only accepts 25 alphabetic characters and \' - starting with alphabetic characters'); - - // First name with alphanumeric characters - await page.locator('#field-input--firstname').fill('123Test'); - await expect(page.locator('div#field-error--firstname')).toHaveText('This field only accepts 25 alphabetic characters and \' - starting with alphabetic characters'); - - // Validate correct first name - await page.locator('#field-input--firstname').fill(''); - await commands.populateFormFields(page, { firstName: 'testFirstname' }); - await page.locator('button[type=submit]').click(); - await expect(page.locator('div > h1')).toHaveText('Thank you, testFirstname!'); - }); - - test('validate last name field', async ({ page }) => { - const commands = new Commands(page); - - // Last name with invalid characters - await page.locator('#field-input--lastname').fill('Test^$%£'); - await expect(page.locator('div#field-error--lastname > span')).toHaveText('This field only accepts 25 alphanumeric characters and , . ( ) / & \' - starting with alphanumeric characters'); - - // Last name with just a space - await page.locator('#field-input--lastname').fill(' '); - await expect(page.locator('div#field-error--lastname > span')).toHaveText('This field only accepts 25 alphanumeric characters and , . ( ) / & \' - starting with alphanumeric characters'); - - // Last name with alphanumeric characters (valid case) - await page.locator('#field-input--lastname').fill('123Test'); - expect(await page.locator('div#field-error--lastname > span').count()).toEqual(0); - - // Validate correct last name - await page.locator('#field-input--lastname').fill(''); - await commands.populateFormFields(page); - await page.locator('button[type=submit]').click(); - await expect(page.locator('div.success-wrapper--inner h1')).toHaveText('Thank you, test!'); - }); -}); diff --git a/playwright-staging/tests/submit/internationalAddressesValidation.spec.js b/playwright-staging/tests/submit/internationalAddressesValidation.spec.js deleted file mode 100644 index f2823116..00000000 --- a/playwright-staging/tests/submit/internationalAddressesValidation.spec.js +++ /dev/null @@ -1,45 +0,0 @@ -// @ts-check -const { expect } = require('@playwright/test'); -const { test } = require('../../browserstack'); -const { Commands } = require('../utils/commands'); - -test.describe('International addresses validation @sanity @nightly-sanity', () => { - test('selecting a non-UK country and entering a non-UK postcode should submit the form', async ({ page }) => { - const commands = new Commands(page); - - // Navigate to the giftaid page - await page.goto(process.env.BASE_URL, { timeout: 30000 }); - await page.waitForLoadState('domcontentloaded'); - - // Click to activate the form and fill in default values - await page.click('#field-label--giftaid'); - await page.fill('#field-input--mobile', '07123456789'); - await page.fill('input#field-input--firstname', 'test'); - await page.fill('input#field-input--lastname', 'user'); - await page.fill('input#field-input--postcode', '30916-395'); // Non-UK postcode - - // Verify error message for UK postcode requirement - await expect(page.locator('div#field-error--postcode > span')).toContainText('Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below.'); - - // Fill in address details - await page.click('a[aria-describedby=field-error--addressDetails]'); - await page.fill('#field-input--address1', '219 Beacon St'); - await page.fill('#field-input--address2', 'Winder'); - await page.fill('input#field-input--town', 'GA'); - - // Select a random non-UK country - const countries = await page.$$eval('select#field-select--country > option', options => options.map(option => option.value)); - const randomCountryCode = countries[Math.floor(Math.random() * countries.length)]; - await page.selectOption('select[name="country"]', { value: randomCountryCode }); - - // Verify that postcode error is resolved after country change - await expect(page.locator('div#field-error--postcode > span')).not.toBeVisible(); - - // Submit the form and ensure no errors are shown for international address - await commands.selectMarketingPrefs(page); // Assuming this handles checkbox interactions - await page.click('button[type=submit]'); - await expect(page.locator('div.success-wrapper--inner h1')).toContainText('Thank you, test!'); - - await page.close(); - }); -}); diff --git a/playwright-staging/tests/submit/marketingPreferencesData.spec.js b/playwright-staging/tests/submit/marketingPreferencesData.spec.js deleted file mode 100644 index 6589b3b0..00000000 --- a/playwright-staging/tests/submit/marketingPreferencesData.spec.js +++ /dev/null @@ -1,63 +0,0 @@ -// @ts-check -const { expect } = require('@playwright/test'); -const { test } = require('../../browserstack'); -const { Commands } = require('../utils/commands'); -const { MarketingPrefsVerify } = require('../utils/marketingPrefsVerify'); - -const Chance = require('chance'); -const chance = new Chance(); - -test('Verify giftaid marketing preferences data in contact-store @sanity @nightly-sanity', async ({ page }) => { - const firstName = chance.first(); - const lastName = chance.last(); - const phone = chance.phone({ country: 'uk', mobile: false }).replace(/\s/g, '') // UK phone number - const mobile = chance.phone({ country: "uk", mobile: true }).replace(/\s/g, '') // UK mobile number - const address1 = chance.street(); - const address2 = chance.street(); - const address3 = chance.city(); - const postcode = 'SE1 7TP'; - const town = chance.county(); - const email = `giftaid-staging-${Date.now().toString()}@email.sls.comicrelief.com`; - - const commands = new Commands(page); - - // Navigate to the giftaid page - await page.goto(process.env.BASE_URL, { timeout: 30000 }); - await page.waitForLoadState('load'); - await page.locator('#field-label--giftaid').click(); - - // Populate all input fields using Commands class - await commands.populateFormFields(page, { - mobile, firstName, lastName, address1, address2, address3, town, postcode, email - }); - - // Select marketing preferences using Commands class - await commands.selectMarketingPrefs(page, { email, phone }); - - // Submit the form - await page.locator('button[type=submit]').click(); - - // Verify success message - await expect(page.locator('div.success-wrapper--inner h1')).toHaveText(`Thank you, ${firstName}!`); - - // Retrieve and verify marketing preferences data - const mpData = await MarketingPrefsVerify.get(email); - - expect(mpData.campaign).toEqual('SR26'); - expect(mpData.firstname).toEqual(firstName); - expect(mpData.lastname).toEqual(lastName); - expect(mpData.email).toEqual(email); - expect(mpData.address1).toEqual(address1); - expect(mpData.town).toEqual(town); - expect(mpData.country).toEqual('GB'); - expect(mpData.transsourceurl).toContain(process.env.BASE_URL); - expect(mpData.transsource).toEqual('SR26_GiftAid'); - expect(mpData.transtype).toEqual('prefs'); - expect(mpData.permissionemail).toEqual('1'); - expect(mpData.permissionsms).toEqual('1'); - expect(mpData.permissionphone).toEqual('1'); - expect(mpData.phone).toEqual(phone); - expect(mpData.mobile).toEqual(mobile.replace(/^[07]{1}/, '+44')); - - await page.close(); -}); diff --git a/playwright-staging/tests/submit/marketingPreferencesValidation.spec.js b/playwright-staging/tests/submit/marketingPreferencesValidation.spec.js deleted file mode 100644 index f7fe5c6e..00000000 --- a/playwright-staging/tests/submit/marketingPreferencesValidation.spec.js +++ /dev/null @@ -1,76 +0,0 @@ -// @ts-check -const { expect } = require('@playwright/test'); -const { test } = require('../../browserstack'); -const { Commands } = require('../utils/commands'); - -const Chance = require('chance'); -const chance = new Chance(); - -const email = `giftaid-update-staging-${chance.email()}`; -const phone = chance.phone({ country: 'uk', mobile: false }).replace(/\s/g, ''); - -test.describe('Marketing preferences validation @sanity @nightly-sanity', () => { - - test.beforeEach(async ({ page }) => { - const commands = new Commands(page); - await page.goto(process.env.BASE_URL, { timeout: 30000 }); - await page.waitForLoadState('domcontentloaded'); - await page.click('#field-label--giftaid'); - await commands.populateFormFields(page); - }); - - test('clicking and unclicking marketing prefs options should submit the giftaid form', async ({ page }) => { - // Interact with marketing preferences - const marketingOptions = ['[aria-label="field-label--Email--Email"]', '[aria-label="field-label--Phone--Phone"]', '[aria-label="field-label--Text--SMS"]']; - for (const option of marketingOptions) { - await page.click(option); - expect(await page.locator(option).isChecked()).toBeTruthy(); - } - - // Enter email and phone to validate the form can still submit - await expect(page.locator('input#field-input--email')).toBeVisible(); - await page.fill('input#field-input--email', email); - await expect(page.locator('input#field-input--phone')).toBeVisible(); - await page.fill('input#field-input--phone', phone); - - // Submit the form - await page.click('button[type=submit]'); - await expect(page.locator('div.success-wrapper--inner h1')).toHaveText('Thank you, test!'); - }); - - test('Validate email marketing preference field', async ({ page }) => { - await page.click('[aria-label="field-label--Email--Email"]'); - await page.fill('input#field-input--email', email); - - // Clear and check for error - await page.fill('input#field-input--email', ''); - await expect(page.locator('#field-error--email')).toHaveText('Please fill in your email address'); - - // Input invalid email and check for error - await page.fill('input#field-input--email', 'example@£$^&email.com'); - await expect(page.locator('#field-error--email')).toHaveText('Please fill in a valid email address'); - - // Re-enter valid email and submit - await page.fill('input#field-input--email', email); - await page.click('button[type=submit]'); - await expect(page.locator('div.success-wrapper--inner h1')).toHaveText('Thank you, test!'); - }); - - test('Validate phone marketing preference field', async ({ page }) => { - await page.click('[aria-label="field-label--Phone--Phone"]'); - await page.fill('input#field-input--phone', phone); - - // Clear and check for error - await page.fill('input#field-input--phone', ''); - await expect(page.locator('div#field-error--phone > span')).toHaveText('Please fill in your phone number'); - - // Input invalid phone number and check for error - await page.fill('input#field-input--phone', '0208569424'); - await expect(page.locator('div#field-error--phone > span')).toHaveText('Please fill in a valid UK phone number, with no spaces'); - - // Re-enter valid phone number and submit - await page.fill('input#field-input--phone', phone); - await page.click('button[type=submit]'); - await expect(page.locator('div.success-wrapper--inner h1')).toHaveText('Thank you, test!'); - }); -}); diff --git a/playwright-staging/tests/submit/postcodeLookup.spec.js b/playwright-staging/tests/submit/postcodeLookup.spec.js deleted file mode 100644 index 740c4b52..00000000 --- a/playwright-staging/tests/submit/postcodeLookup.spec.js +++ /dev/null @@ -1,50 +0,0 @@ -// @ts-check -const { expect } = require('@playwright/test'); -const { test } = require('../../browserstack'); - -test.describe('Postcode validation @sanity @nightly-sanity', () => { - - test.beforeEach(async ({ page }) => { - await page.goto(process.env.BASE_URL, { timeout: 30000 }); - await page.waitForLoadState('domcontentloaded'); - await page.click('#field-label--giftaid'); - await page.fill('#field-input--mobile', '07123456789'); - await page.fill('input#field-input--firstname', 'test'); - await page.fill('input#field-input--lastname', 'user'); - }); - - test('Postcode formatting errors', async ({ page }) => { - const postcodes = [ - { code: 'S E 1 7 T P', message: 'Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below.' }, - { code: 'SE$%TP', message: 'Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below.' } - ]; - - for (const { code, message } of postcodes) { - await page.fill('input#field-input--postcode', code); - await expect(page.locator('div#field-error--postcode > span')).toBeVisible(); - await expect(page.locator('div#field-error--postcode > span')).toContainText(message); - } - await page.close(); - }); - - test('enter valid UK postcode using postcode lookup should be able to submit the form', async ({ page }) => { - await page.fill('input#field-input--postcode', 'SE1 7TP'); - await page.click('#postcode_button'); - - if (await page.isVisible('#field-select--addressSelect')) { - await page.selectOption('select#field-select--addressSelect', { label: 'COMIC RELIEF, CAMELFORD HOUSE 87-90' }); - await expect(page.locator('input#field-input--postcode')).toHaveValue('SE1 7TP'); - } else { - await page.click('a[aria-describedby=field-error--addressDetails]'); - await page.fill('#field-input--address1', 'COMIC RELIEF'); - await page.fill('#field-input--address2', 'CAMELFORD HOUSE 87-90'); - await page.fill('#field-input--address3', 'ALBERT EMBANKMENT'); - await page.fill('#field-input--town', 'LONDON'); - } - - await page.click('button[type=submit]'); - await expect(page.locator('div.success-wrapper--inner h1')).toContainText('Thank you,\n' + 'test!'); - - await page.close(); - }); -}); diff --git a/playwright-staging/tests/submit/sorry.spec.js b/playwright-staging/tests/submit/sorry.spec.js deleted file mode 100644 index c331d787..00000000 --- a/playwright-staging/tests/submit/sorry.spec.js +++ /dev/null @@ -1,19 +0,0 @@ -// @ts-check -const { expect } = require('@playwright/test'); -const { test } = require('../../browserstack'); - -test('Accessing giftaid sorry page should show the sorry message @sanity @nightly-sanity', async ({ page }) => { - // Go directly to the "sorry" page appended to the base URL - await page.goto(`${process.env.BASE_URL}sorry`, { timeout: 30000 }); - - // Wait for the main header to be loaded and visible on the page - const header = page.locator('div > h1'); - await expect(header).toBeVisible(); - await expect(header).toContainText('Sorry!'); - - // Verify that the first paragraph under the div > div is visible - const firstParagraph = page.locator('div > div > p:nth-child(1)'); - await expect(firstParagraph).toBeVisible(); - - await page.close(); -}); diff --git a/playwright-staging/tests/submit/successRedirect.spec.js b/playwright-staging/tests/submit/successRedirect.spec.js deleted file mode 100644 index 686b980c..00000000 --- a/playwright-staging/tests/submit/successRedirect.spec.js +++ /dev/null @@ -1,20 +0,0 @@ -// @ts-check -const { expect } = require('@playwright/test'); -const { test } = require('../../browserstack'); - -test('Accessing success page should redirect to giftaid homepage @sanity @nightly-sanity', async ({ page }) => { - // Navigate directly to the success page and expect a redirect - await page.goto(`${process.env.BASE_URL}success`, { timeout: 30000 }); - await page.waitForLoadState('domcontentloaded'); - - // Check if the expected header title is present, which indicates a successful redirect - const headerTitle = page.locator('h1.giftaid-title'); - await expect(headerTitle).toBeVisible(); - await expect(headerTitle).toContainText('Giftaid it'); - - // Verify the presence of key elements on the redirected homepage - await expect(page.locator('#field-label--giftaid')).toBeVisible(); - await expect(page.locator('#field-input--mobile')).toBeVisible(); - - await page.close(); -}); diff --git a/playwright-staging/tests/submit/validFormSubmission.spec.js b/playwright-staging/tests/submit/validFormSubmission.spec.js deleted file mode 100644 index d9264b86..00000000 --- a/playwright-staging/tests/submit/validFormSubmission.spec.js +++ /dev/null @@ -1,32 +0,0 @@ -// @ts-check -const { expect } = require('@playwright/test'); -const { test } = require('../../browserstack'); -const { Commands } = require('../utils/commands'); - -test('Valid giftaid submission @sanity @nightly-sanity', async ({ page }) => { - const commands = new Commands(page); - - // Navigate to the giftaid page - await page.goto(process.env.BASE_URL, { timeout: 30000 }); - await page.waitForLoadState('domcontentloaded'); - - // Click the Giftaid checkbox - await page.click('#field-label--giftaid'); - - // Populate all the form fields with valid inputs - await commands.populateFormFields(page); - - // Select marketing preferences as required - await commands.selectMarketingPrefs(page); - - // Submit the form and wait for the navigation to ensure the submission goes through - await Promise.all([ - page.waitForNavigation(), // This ensures that the navigation happens before the check - page.click('button[type=submit]') - ]); - - // Check for the thank you message to confirm successful submission - await expect(page.locator('div.success-wrapper--inner h1')).toHaveText('Thank you, test!'); - - await page.close(); -}); diff --git a/playwright-staging/tests/support/hooks.js b/playwright-staging/tests/support/hooks.js new file mode 100644 index 00000000..72796d4e --- /dev/null +++ b/playwright-staging/tests/support/hooks.js @@ -0,0 +1,67 @@ +require('dotenv').config(); +const caps = require('../../config/browserstack'); +const { Before, After, Status, setDefaultTimeout } = require('@cucumber/cucumber'); +const { chromium } = require('@playwright/test'); +const Commands = require('../utils/commands'); + +setDefaultTimeout(300 * 1000); // 5 mins + +// Runs before each scenario +Before(async function (scenario) { + // Set BrowserStack session name to scenario name (helps to see in BS dashboard) + caps.name = scenario.pickle.name; + + // Connect to BrowserStack via Playwright CDP + this.browser = await chromium.connect({ + wsEndpoint: `wss://cdp.browserstack.com/playwright?caps=${encodeURIComponent(JSON.stringify(caps))}`, + }); + + this.context = await this.browser.newContext({ + viewport: null, // Use full screen instead of fixed viewport + serviceWorkers: 'block', // Avoids caching issues + }); + + this.page = await this.context.newPage(); + + // Keep default Playwright action/assertion timeout consistent across Cucumber tests + this.page.setDefaultTimeout(30000); + + // Increase navigation timeout for slower redirects/page loads on BrowserStack + this.page.setDefaultNavigationTimeout(45000); + + // Maximise browser window + const session = await this.context.newCDPSession(this.page); + const { windowId } = await session.send('Browser.getWindowForTarget'); + await session.send('Browser.setWindowBounds', { + windowId, + bounds: { windowState: 'maximized' }, + }); + + /** @type {Commands} */ + this.commands = new Commands(this.page); +}); + +// After each scenario completes, report the test result (pass/fail) to BrowserStack +After(async function (scenario) { + const testResult = { + action: 'setSessionStatus', + arguments: { + status: scenario.result?.status === Status.PASSED ? 'passed' : 'failed', + reason: scenario.result?.message || '', + }, + }; + + // Close page + if (this.page) { + await this.page.evaluate(() => {}, `browserstack_executor: ${JSON.stringify(testResult)}`); + await this.page.close(); + } + // Close context and browser + if (this.context) { + await this.context.close(); + } + + if (this.browser) { + await this.browser.close(); + } +}); diff --git a/playwright-staging/tests/support/world.js b/playwright-staging/tests/support/world.js new file mode 100644 index 00000000..bc162f1f --- /dev/null +++ b/playwright-staging/tests/support/world.js @@ -0,0 +1,14 @@ +// This defines shared context for each scenario +// Values stored here (browser, page, commands) are accessible across all step definitions via "this" +const { setWorldConstructor } = require('@cucumber/cucumber'); + +class CustomWorld { + constructor() { + this.browser = null; + this.context = null; + this.page = null; + this.commands = null; + } +} + +setWorldConstructor(CustomWorld); diff --git a/playwright-staging/tests/update/formValidation.spec.js b/playwright-staging/tests/update/formValidation.spec.js deleted file mode 100644 index 0c71fe80..00000000 --- a/playwright-staging/tests/update/formValidation.spec.js +++ /dev/null @@ -1,181 +0,0 @@ -// @ts-check -const { expect } = require('@playwright/test'); -const { test } = require('../../browserstack'); -const { Commands } = require('../utils/commands'); -const { v4: uuidv4 } = require('uuid'); -const Chance = require('chance'); -const chance = new Chance(); - -test.describe('Giftaid Update form validation @sanity @nightly-sanity', () => { - let commands; - - test.beforeEach(async ({ page }) => { - commands = new Commands(page); - // Navigate to the Giftaid Update form - await page.goto(`${process.env.BASE_URL}update`, { timeout: 30000 }); - await page.waitForLoadState('domcontentloaded'); - }); - - test('empty input fields should show error messages', async ({ page }) => { - // Submit the form without filling out any fields - await page.click('button[type=submit]'); - - // Check for the error messages associated with each field - await expect(page.locator('div#field-error--firstname > span')).toHaveText('Please fill in your first name'); - await expect(page.locator('div#field-error--lastname > span')).toHaveText('Please fill in your last name'); - await expect(page.locator('div#field-error--email > span')).toHaveText('Please fill in your email address'); - await expect(page.locator('div#field-error--postcode > span')).toHaveText('Please enter your postcode'); - await expect(page.locator('div#field-error--addressDetails > span')).toHaveText('Please fill in your address'); - await expect(page.locator('div#field-error--giftAidClaimChoice > span')).toHaveText('This field is required'); - await page.close(); - }); - - test('Validate first name field on Giftaid update form', async ({ page }) => { - const commands = new Commands(page); - - // Set up different first name test cases - const firstNameTestCases = [ - { input: 'Test^$%£', error: "This field only accepts alphabetic characters and ' -" }, // Test for invalid characters - { input: ' ', error: "This field only accepts alphabetic characters and ' -" }, // Test for space as input - { input: '123Test', error: "This field only accepts alphabetic characters and ' -" } // Test for alphanumeric input - ]; - - for (let testCase of firstNameTestCases) { - await page.fill('#field-input--firstname', testCase.input); - if (testCase.input) { - await page.keyboard.press('Enter'); // Trigger validation by clicking submit button - } - await expect(page.locator('#field-error--firstname')).toHaveText(testCase.error); - } - - // Test for a valid first name - await page.fill('#field-input--firstname', ''); // clear firstname field - await commands.populateUpdateFormFields(page, { firstName: 'John' }); - await page.click('#giftAidClaimChoice>div:nth-child(2)>label'); // Select yes for declaration - await page.click('button[type=submit]'); // Submit the form - - await expect(page.locator('div.success-wrapper--inner h1')).toHaveText('Thank you, John!'); - await page.close(); - }); - - test('Validate email field on giftaid update form', async ({ page }) => { - const commands = new Commands(page); - - // Set up different email test cases - const emailTestCases = [ - { input: 'test@comic$relief.com', error: 'Please fill in a valid email address', visible: true }, - { input: 'test@c{(micrelief.com', error: 'Please fill in a valid email address', visible: true }, - { input: 'test@comic%relief.com', error: 'Please fill in a valid email address', visible: true }, - { input: 'te$%^st@comicrelief.com', error: '', visible: false }, - { input: 'Test0-9!#$%&\'*+/=?^_{|}~-@comicrelief_9-8.com.uk', error: 'Please fill in a valid email address', visible: true } - ]; - - for (let testCase of emailTestCases) { - await page.fill('input#field-input--email', ''); // clear the email field before entering the test cases input - await page.fill('input#field-input--email', testCase.input); - await page.keyboard.press('Enter'); // Trigger validation by clicking the submit button - if (testCase.visible) { - await expect(page.locator('div#field-error--email > span')).toBeVisible(); - await expect(page.locator('div#field-error--email > span')).toHaveText(testCase.error); - } else { - await expect(page.locator('div#field-error--email > span')).not.toBeVisible(); - } - } - - // Test for a valid email - const validEmail = `giftaid-update-staging-${chance.email()}`; - await page.fill('input#field-input--email', ''); // clear email field - await commands.populateUpdateFormFields(page, { email: validEmail }); - await page.click('#giftAidClaimChoice>div:nth-child(3)>label'); // Select no for declaration - await page.click('button[type=submit]'); // Submit the form - - await expect(page.locator('div.success-wrapper--inner h1')).toHaveText('Thanks for letting us know'); - await page.close(); - }); - - test('Validate mobile number field on giftaid update form', async ({ page }) => { - const commands = new Commands(page); - // List of allowed prefixes for UK mobile numbers - const prefixes = ['071', '073', '074', '075', '077', '078', '079']; - // Randomly select one prefix from the list - const prefix = chance.pickone(prefixes); - // Generate the remaining 8 digits randomly - const mobile = `${prefix}${chance.string({ pool: '0123456789', length: 8 })}`; - console.log('mobile number generated', mobile); - - // Test cases for various mobile number validations - const mobileTestCases = [ - { input: '0722345678', error: 'Please enter a valid mobile phone number - it must be the same number associated with your donation.' }, - { input: '0722345678900', error: 'Please enter a valid mobile phone number - it must be the same number associated with your donation.' }, - { input: '0722 345 6789', error: 'Please enter a valid mobile phone number - it must be the same number associated with your donation.' }, - { input: '0780ab5694245', error: 'Please enter a valid mobile phone number - it must be the same number associated with your donation.' }, - { input: '07123456789', valid: true }, - { input: '07340707252', valid: true }, - ]; - - for (let testCase of mobileTestCases) { - await page.locator('#field-input--mobile').fill(''); // Clear the field before each test - await page.locator('#field-input--mobile').type(testCase.input, { delay: 100 }); - if (testCase.valid) { - await expect(page.locator('div#field-error--mobile > span')).not.toBeVisible(); - } else { - await expect(page.locator('div#field-error--mobile > span')).toHaveText(testCase.error); - } - } - - // Validate correct mobile number - await page.locator('#field-input--mobile').fill(''); // Ensure the field is cleared and filled with valid data - await commands.populateUpdateFormFields(page, { lastName: 'test', mobile: mobile }); - await page.click('#giftAidClaimChoice>div:nth-child(2)>label'); // Select yes for declaration - await page.click('button[type=submit]'); // Submit the form - - await expect(page.locator('div.success-wrapper--inner h1')).toHaveText('Thank you, test!'); - }); - - test('Postcode validation and form submission', async ({ page }) => { - // Define postcodes and expected error messages - const postcodes = [ - { input: 'S E 1 7 T P', error: 'Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below.' }, - { input: 'SE$%TP', error: 'Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below.' }, - { input: 'cro 7tp', error: 'Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below.' } - ]; - - // Test for each invalid postcode - for (const postcode of postcodes) { - await page.fill('input#field-input--postcode', ''); - await page.type('input#field-input--postcode', postcode.input); - await expect(page.locator('div#field-error--postcode > span')).toBeVisible(); - await expect(page.locator('div#field-error--postcode > span')).toHaveText(postcode.error); - } - - // Test for a valid postcode and subsequent form submission - await page.fill('input#field-input--postcode', 'SE1 7TP'); - await page.click('#postcode_button'); - - // Checking whether address selection is available or if manual entry is needed - if (await page.locator('#field-select--addressSelect').isVisible()) { - // Select the first address if available - const options = await page.$$eval('#field-select--addressSelect option', options => options.map(option => option.value)); - await page.selectOption('#field-select--addressSelect', options[1]); - await page.click('button[type=submit]'); - } else { - // Fallback to manual address input if no selection is available - await page.click('a[aria-describedby=field-error--addressDetails]'); - await page.fill('#field-input--address1', 'COMIC RELIEF'); - await page.fill('#field-input--address2', 'CAMELFORD HOUSE 87-90'); - await page.fill('#field-input--address3', 'ALBERT EMBANKMENT'); - await page.fill('input#field-input--town', 'LONDON'); - await page.click('button[type=submit]'); - } - - await page.locator('input#field-input--firstname').fill('test'); - await page.locator('input#field-input--lastname').fill(chance.last()); - await page.locator('input#field-input--email').fill(`giftaid-update-staging-${chance.email()}`); - await page.fill('input#field-input--postcode', 'SE1 7TP'); - await page.click('#giftAidClaimChoice>div:nth-child(2)>label'); // Select yes for declaration - await page.click('button[type=submit]'); // Submit the form - - await expect(page.locator('div.success-wrapper--inner h1')).toHaveText('Thank you, test!'); - await page.close(); - }); -}); diff --git a/playwright-staging/tests/update/giftaidDeclarationOptions.spec.js b/playwright-staging/tests/update/giftaidDeclarationOptions.spec.js deleted file mode 100644 index 397ba1f1..00000000 --- a/playwright-staging/tests/update/giftaidDeclarationOptions.spec.js +++ /dev/null @@ -1,29 +0,0 @@ -// @ts-check -const { expect } = require('@playwright/test'); -const { test } = require('../../browserstack'); -const { Commands } = require('../utils/commands'); - -test('Validate Giftaid declaration claim selections @sanity @nightly-sanity', async ({ page }) => { - const commands = new Commands(page); - await page.goto(process.env.BASE_URL + 'update', { timeout: 30000 }); - await page.waitForLoadState('domcontentloaded'); - - // Populate fields and submit the form to get to the Giftaid declaration part - await commands.populateUpdateFormFields(page); - await page.locator('button[type=submit]').click(); - - // Select 'Yes' for Giftaid declaration and verify it is selected - await page.locator('#giftAidClaimChoice>div:nth-child(2)>label').click(); - expect(await page.locator('#giftAidClaimChoice>div:nth-child(2)>input').isChecked()).toBeTruthy(); - expect(await page.locator('#giftAidClaimChoice>div:nth-child(3)>input').isChecked()).toBeFalsy(); - - // Select 'No' for Giftaid declaration and verify it is selected - await page.locator('#giftAidClaimChoice>div:nth-child(3)>label').click(); - expect(await page.locator('#giftAidClaimChoice>div:nth-child(3)>input').isChecked()).toBeTruthy(); - expect(await page.locator('#giftAidClaimChoice>div:nth-child(2)>input').isChecked()).toBeFalsy(); - - await page.locator('button[type=submit]').click(); - await expect(page.locator('div.success-wrapper--inner h1')).toHaveText('Thanks for letting us know'); - - await page.close(); -}); diff --git a/playwright-staging/tests/update/internationalAddressesValidation.spec.js b/playwright-staging/tests/update/internationalAddressesValidation.spec.js deleted file mode 100644 index 9f64900d..00000000 --- a/playwright-staging/tests/update/internationalAddressesValidation.spec.js +++ /dev/null @@ -1,53 +0,0 @@ -// @ts-check -const { expect } = require('@playwright/test'); -const { test } = require('../../browserstack'); -const { v4: uuidv4 } = require('uuid'); - -test.describe('International addresses validation on update form @sanity @nightly-sanity', () => { - test('selecting a non-UK country and entering a non-UK postcode should submit the update form', async ({ page }) => { - - await page.goto(process.env.BASE_URL + 'update', { timeout: 30000 }); - await page.waitForLoadState('domcontentloaded'); - - // fill in all input fields - // await page.locator('input#field-input--transactionId').fill(transactionId); - await page.locator('#field-input--firstname').fill('test'); - await page.locator('#field-input--lastname').fill('test lastname'); - await page.locator('input#field-input--email').fill('giftaid-staging-@email.sls.comicrelief.com'); - - // enter a non-UK postcode and attempt to validate it - await page.locator('input#field-input--postcode').fill('30916-395'); - await expect(page.locator('div#field-error--postcode > span')).toContainText('Please enter a valid UK postcode, using a space. For non-UK addresses, please use manual entry below.'); - - // manually enter international address details - await page.locator('a[aria-describedby=field-error--addressDetails]').click(); - await page.locator('#field-input--address1').fill('219 Beacon St'); - await page.locator('#field-input--address2').fill('Winder'); - await page.locator('#field-input--address3').fill('Park Ridge'); - await page.locator('#field-input--town').fill('GA'); - - // Select a random country from the dropdown (excluding UK to simulate international address) - const countryOptions = await page.$$eval('select#field-select--country>option', options => - options.map(option => option.value).filter(value => value !== 'GB') - ); - const randomCountryCode = countryOptions[Math.floor(Math.random() * countryOptions.length)]; - await page.locator('select[name="country"]').selectOption({ value: randomCountryCode }); - - // Wait for the form to adjust to the selected country - await page.waitForTimeout(2000); - - // When an international country is selected, the postcode error for UK format should not show anymore - await expect(page.locator('div#field-error--postcode > span')).not.toBeVisible(); - - // Select yes for giftaid declaration to complete the form - await page.locator('#giftAidClaimChoice>div:nth-child(2)>label').click(); - - // Submitting the form with valid international details - await page.locator('button[type=submit]').click(); - - // Thank you message on success page - await expect(page.locator('div.success-wrapper--inner h1')).toContainText('Thank you, test!'); - - await page.close(); - }); -}); diff --git a/playwright-staging/tests/update/sorry.spec.js b/playwright-staging/tests/update/sorry.spec.js deleted file mode 100644 index e52df692..00000000 --- a/playwright-staging/tests/update/sorry.spec.js +++ /dev/null @@ -1,19 +0,0 @@ -// @ts-check -const { expect } = require('@playwright/test'); -const { test } = require('../../browserstack'); - -test('Accessing giftaid update sorry page should show the sorry message @sanity @nightly-sanity', async ({ page }) => { - // Navigate to the 'Sorry' page of giftaid update form - await page.goto(`${process.env.BASE_URL}update/sorry`, { timeout: 30000 }); - await page.waitForLoadState('domcontentloaded'); - - // Check for the 'Sorry' message header - const headerText = await page.locator('div > h1').textContent(); - expect(headerText).toContain('Sorry!'); - - // Verify the sorry message - const sorryMessage = await page.locator('div > div > p:nth-child(1)').isVisible(); - expect(sorryMessage).toBeTruthy(); - - await page.close(); -}); diff --git a/playwright-staging/tests/update/updateSuccessRedirect.spec.js b/playwright-staging/tests/update/updateSuccessRedirect.spec.js deleted file mode 100644 index 098bb7b0..00000000 --- a/playwright-staging/tests/update/updateSuccessRedirect.spec.js +++ /dev/null @@ -1,17 +0,0 @@ -// @ts-check -const { expect } = require('@playwright/test'); -const { test } = require('../../browserstack'); - -test.describe('Success page redirect @sanity @nightly-sanity', () => { - test('Accessing success page should redirect to giftaid update homepage', async ({ page }) => { - // Navigate to the success page of the Giftaid update directly - await page.goto(`${process.env.BASE_URL}update/success`, { timeout: 30000 }); - await page.waitForLoadState('domcontentloaded'); - - // Confirm the page has the expected Giftaid title after redirection - const pageTitle = await page.locator('h1[class="giftaid-title"]').textContent(); - expect(pageTitle).toContain('Giftaid it'); - - await page.close(); - }); -}); diff --git a/playwright-staging/tests/update/validFormSubmission.spec.js b/playwright-staging/tests/update/validFormSubmission.spec.js deleted file mode 100644 index dcfc71f7..00000000 --- a/playwright-staging/tests/update/validFormSubmission.spec.js +++ /dev/null @@ -1,24 +0,0 @@ -// @ts-check -const { expect } = require('@playwright/test'); -const { test } = require('../../browserstack'); -const { Commands } = require('../utils/commands'); - -test('Valid giftaid update submission @sanity @nightly-sanity', async ({ page }) => { - const commands = new Commands(page); - - // Navigate to the Giftaid update page - await page.goto(`${process.env.BASE_URL}update`, { timeout: 30000 }); - await page.waitForLoadState('domcontentloaded'); - - // Populate all input fields with valid data - await commands.populateUpdateFormFields(page); - - // Select 'Yes' for GiftAid declaration - await page.locator('#giftAidClaimChoice>div:nth-child(2)>label').click(); - - // Submit the form and validate the thank you message - await page.locator('button[type=submit]').click(); - await expect(page.locator('div.success-wrapper--inner h1')).toContainText('Thank you, test!'); - - await page.close(); -}); diff --git a/playwright-staging/tests/utils/commands.js b/playwright-staging/tests/utils/commands.js index 92a7cf78..e18820e4 100644 --- a/playwright-staging/tests/utils/commands.js +++ b/playwright-staging/tests/utils/commands.js @@ -1,3 +1,4 @@ +const { selectors } = require('./locators'); const Chance = require('chance'); const chance = new Chance(); @@ -15,11 +16,10 @@ class Commands { } /** - * Populate giftaid from fields - * @param page - Playwright page object. + * Populate giftaid form fields * @param userData - Optional user data for form filling. */ - async populateFormFields(page, { + async populateFormFields({ mobile = chance.phone({ country: 'uk', mobile: true }).replace(/\s/g, ''), // Remove spaces from the phone number firstName = 'test', lastName = chance.last(), @@ -29,39 +29,39 @@ class Commands { address3 = 'test address 3', town = chance.city(), } = {}) { - await page.locator('#field-input--mobile').type(mobile); - await page.locator('input#field-input--firstname').type(firstName); - await page.locator('input#field-input--lastname').type(lastName); - await page.locator('input#field-input--postcode').type(postcode); - await page.locator('a[aria-describedby=field-error--addressDetails]').click(); - await page.locator('input#field-input--address1').type(address1); - await page.locator('input#field-input--address2').type(address2); - await page.locator('input#field-input--address3').type(address3); - await page.locator('input#field-input--town').type(town); + await this.page.locator(selectors.formFields.mobile).fill(mobile); + await this.page.locator(selectors.formFields.firstName).fill(firstName); + await this.page.locator(selectors.formFields.lastName).fill(lastName); + await this.page.locator(selectors.formFields.postcode).fill(postcode); + await this.page.locator(selectors.address.manualAddressLink).click(); + await this.page.locator(selectors.address.address1).fill(address1); + await this.page.locator(selectors.address.address2).fill(address2); + await this.page.locator(selectors.address.address3).fill(address3); + await this.page.locator(selectors.address.town).fill(town); } /** * Select marketing preferences opt ins - * @param page - Playwright page object. * @param options - Optional marketing preferences. */ - async selectMarketingPrefs(page, { - email = `giftaid-staging-${chance.email()}`, - phone = chance.phone({ country: 'uk', mobile: false }).replace(/\s/g, '') // UK phone number + async selectMarketingPrefs({ + email = `giftaid-staging-${Date.now()}@email.sls.comicrelief.com`, + phone = chance.phone({ country: 'uk', mobile: false }).replace(/\s/g, '') } = {}) { - await page.locator('#field-wrapper--Email > div').click(); - await page.locator('input#field-input--email').type(email); - await page.locator('#field-wrapper--Phone > div').click(); - await page.locator('input#field-input--phone').type(phone, { delay: 200 }); - await page.locator('input#field-label--Text--SMS').click(); + await this.page.locator(selectors.marketingPreferences.options.email).click(); + await this.page.locator(selectors.marketingPreferences.fields.email).fill(email); + + await this.page.locator(selectors.marketingPreferences.options.phone).click(); + await this.page.locator(selectors.marketingPreferences.fields.phone).fill(phone); + + await this.page.locator(selectors.marketingPreferences.options.text).check({ force: true }); } /** - * Populate giftaid update from fields - * @param page - Playwright page object. + * Populate giftaid update form fields * @param userData - Optional user data for form filling. */ - async populateUpdateFormFields(page, { + async populateUpdateFormFields({ firstName = 'test', lastName = chance.last(), email = `giftaid-update-staging-${chance.email()}`, @@ -72,17 +72,17 @@ class Commands { address3 = 'test address 3', town = chance.city(), } = {}) { - await page.locator('input#field-input--firstname').fill(firstName); - await page.locator('input#field-input--lastname').fill(lastName); - await page.locator('input#field-input--postcode').fill(postcode); - await page.locator('input#field-input--email').fill(email); - await page.locator('#field-input--mobile').fill(mobile); - await page.locator('a[aria-describedby=field-error--addressDetails]').click(); - await page.locator('input#field-input--address1').fill(address1); - await page.locator('input#field-input--address2').fill(address2); - await page.locator('input#field-input--address3').fill(address3); - await page.locator('input#field-input--town').fill(town); + await this.page.locator(selectors.formFields.firstName).fill(firstName); + await this.page.locator(selectors.formFields.lastName).fill(lastName); + await this.page.locator(selectors.formFields.postcode).fill(postcode); + await this.page.locator(selectors.formFields.email).fill(email); + await this.page.locator(selectors.formFields.mobile).fill(mobile); + await this.page.locator(selectors.address.manualAddressLink).click(); + await this.page.locator(selectors.address.address1).fill(address1); + await this.page.locator(selectors.address.address2).fill(address2); + await this.page.locator(selectors.address.address3).fill(address3); + await this.page.locator(selectors.address.town).fill(town); } } -module.exports = { Commands }; +module.exports = Commands; diff --git a/playwright-staging/tests/utils/locators.js b/playwright-staging/tests/utils/locators.js new file mode 100644 index 00000000..2fb95d0c --- /dev/null +++ b/playwright-staging/tests/utils/locators.js @@ -0,0 +1,75 @@ +const selectors = { + giftaid: { + option: '#field-label--giftaid', + }, + + homepage: { + heading: 'h1.giftaid-title', + }, + + giftAidClaimChoice: { + yes: '#giftAidClaimChoice>div:nth-child(2)>label', + no: '#giftAidClaimChoice>div:nth-child(3)>label', + yesInput: '#giftAidClaimChoice>div:nth-child(2)>input', + noInput: '#giftAidClaimChoice>div:nth-child(3)>input', + }, + + formFields: { + mobile: '#field-input--mobile', + firstName: 'input#field-input--firstname', + lastName: 'input#field-input--lastname', + email: 'input#field-input--email', + postcode: 'input#field-input--postcode', + postcodeLookup: '#postcode_button', + submitButton: 'button[type=submit]', + }, + + address: { + manualAddressLink: 'a[aria-describedby=field-error--addressDetails]', + addressSelect: '#field-select--addressSelect', + address1: '#field-input--address1', + address2: '#field-input--address2', + address3: '#field-input--address3', + town: '#field-input--town', + country: 'select#field-select--country', + countryByName: 'select[name="country"]', + countryDropdown: 'select#field-select--country', + countryOptions: 'select#field-select--country > option', + }, + + marketingPreferences: { + options: { + email: '[aria-label="field-label--Email--Email"]', + phone: '[aria-label="field-label--Phone--Phone"]', + text: '[aria-label="field-label--Text--SMS"]', + }, + fields: { + email: 'input#field-input--email', + phone: 'input#field-input--phone', + }, + }, + + errorMessages: { + mobile: 'div#field-error--mobile > span', + firstName: 'div#field-error--firstname > span', + lastName: 'div#field-error--lastname > span', + email: '#field-error--email > span', + phone: 'div#field-error--phone > span', + postcode: 'div#field-error--postcode > span', + addressSelect: 'div#field-error--addressSelect > span', + address1: '#field-error--address1 > span', + town: '#field-error--town > span', + addressDetails: 'div#field-error--addressDetails > span', + giftAidClaimChoice: 'div#field-error--giftAidClaimChoice > span', + }, + + success: { + heading: 'h1', + }, + sorry: { + heading: 'div > h1', + firstParagraph: 'div > div > p:nth-child(1)', + }, +}; + +module.exports = { selectors }; diff --git a/playwright-staging/yarn.lock b/playwright-staging/yarn.lock index 8a3061cc..c9132af3 100644 --- a/playwright-staging/yarn.lock +++ b/playwright-staging/yarn.lock @@ -9,6 +9,16 @@ dependencies: regenerator-runtime "^0.13.11" +"@babel/runtime@^7.15.4": + version "7.29.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.29.2.tgz#9a6e2d05f4b6692e1801cd4fb176ad823930ed5e" + integrity sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g== + +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + "@comicrelief/data-models@^1.29.6": version "1.65.0" resolved "https://registry.yarnpkg.com/@comicrelief/data-models/-/data-models-1.65.0.tgz#2641ed99e58edf426e15ec7c4d62c73676d94dcb" @@ -24,6 +34,124 @@ resolved "https://registry.yarnpkg.com/@comicrelief/test-utils/-/test-utils-1.5.15.tgz#48ee30ccfa44d220dabce8dd7e9bb43c8e043814" integrity sha512-XsShlQcGzHDv6FVlJLvWGDPdY4mgqFNWdw+dXlGFXCCiC2WCpVQzVdzdInOTfvCZZXLAHDTT+RaF+RZlj3VVRQ== +"@cucumber/ci-environment@9.1.0": + version "9.1.0" + resolved "https://registry.yarnpkg.com/@cucumber/ci-environment/-/ci-environment-9.1.0.tgz#6d868141c7cfd616931f14723e122a1069401998" + integrity sha512-jdnF6APXP3GawMue8kdMxhu6TBhyRUO4KDRxTowf06NtclLjIw2Ybpo9IcIOMvE8kHukvJyM00uxWX+CfS7JgQ== + +"@cucumber/cucumber-expressions@16.1.0": + version "16.1.0" + resolved "https://registry.yarnpkg.com/@cucumber/cucumber-expressions/-/cucumber-expressions-16.1.0.tgz#2a2538b775e83b84a275ea10f618242717937968" + integrity sha512-Q/tKDNje9RrcOXF2TO2NwTW92rzk+RwKkhYYKLxQT26Co8Qbjom0Cz02HsCMA2wjJ8dw6/d2IbWgiOay9RQA+w== + dependencies: + regexp-match-indices "1.0.2" + +"@cucumber/cucumber@8.9.1": + version "8.9.1" + resolved "https://registry.yarnpkg.com/@cucumber/cucumber/-/cucumber-8.9.1.tgz#6b3bb74e5811d69aee85eaac0298218cb38ab6c7" + integrity sha512-Ir0yloCGu25mnllHunOAaOVXIKrRSE0ICF1FleoJzeupgYWK/x8o6hjOGRJf1Ui17GoRs1CfkP44/K29lmpMtA== + dependencies: + "@cucumber/ci-environment" "9.1.0" + "@cucumber/cucumber-expressions" "16.1.0" + "@cucumber/gherkin" "25.0.2" + "@cucumber/gherkin-streams" "5.0.1" + "@cucumber/gherkin-utils" "8.0.2" + "@cucumber/html-formatter" "20.2.0" + "@cucumber/message-streams" "4.0.1" + "@cucumber/messages" "20.0.0" + "@cucumber/tag-expressions" "4.1.0" + assertion-error-formatter "^3.0.0" + capital-case "^1.0.4" + chalk "^4.1.2" + cli-table3 "0.6.3" + commander "^9.0.0" + debug "^4.3.4" + duration "^0.2.2" + durations "^3.4.2" + error-stack-parser "^2.1.4" + figures "^3.2.0" + glob "^7.1.6" + has-ansi "^4.0.1" + indent-string "^4.0.0" + is-installed-globally "^0.4.0" + is-stream "^2.0.0" + knuth-shuffle-seeded "^1.0.6" + lodash.merge "^4.6.2" + lodash.mergewith "^4.6.2" + mz "^2.7.0" + progress "^2.0.3" + resolve-pkg "^2.0.0" + semver "7.3.8" + string-argv "^0.3.1" + strip-ansi "6.0.1" + supports-color "^8.1.1" + tmp "^0.2.1" + util-arity "^1.1.0" + verror "^1.10.0" + xmlbuilder "^15.1.1" + yup "^0.32.11" + +"@cucumber/gherkin-streams@5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@cucumber/gherkin-streams/-/gherkin-streams-5.0.1.tgz#8c2142d295cd05644456be7282b4bd756c95c4cd" + integrity sha512-/7VkIE/ASxIP/jd4Crlp4JHXqdNFxPGQokqWqsaCCiqBiu5qHoKMxcWNlp9njVL/n9yN4S08OmY3ZR8uC5x74Q== + dependencies: + commander "9.1.0" + source-map-support "0.5.21" + +"@cucumber/gherkin-utils@8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@cucumber/gherkin-utils/-/gherkin-utils-8.0.2.tgz#deae231f84e91f120501d22187c66d36e6c6b59f" + integrity sha512-aQlziN3r3cTwprEDbLEcFoMRQajb9DTOu2OZZp5xkuNz6bjSTowSY90lHUD2pWT7jhEEckZRIREnk7MAwC2d1A== + dependencies: + "@cucumber/gherkin" "^25.0.0" + "@cucumber/messages" "^19.1.4" + "@teppeis/multimaps" "2.0.0" + commander "9.4.1" + source-map-support "^0.5.21" + +"@cucumber/gherkin@25.0.2", "@cucumber/gherkin@^25.0.0": + version "25.0.2" + resolved "https://registry.yarnpkg.com/@cucumber/gherkin/-/gherkin-25.0.2.tgz#e430879f01978d1f9e7a7aa0563031a3a36022e7" + integrity sha512-EdsrR33Y5GjuOoe2Kq5Y9DYwgNRtUD32H4y2hCrT6+AWo7ibUQu7H+oiWTgfVhwbkHsZmksxHSxXz/AwqqyCRQ== + dependencies: + "@cucumber/messages" "^19.1.4" + +"@cucumber/html-formatter@20.2.0": + version "20.2.0" + resolved "https://registry.yarnpkg.com/@cucumber/html-formatter/-/html-formatter-20.2.0.tgz#20857efec721fbc5d64cc31b0107575db1e58651" + integrity sha512-apcxS5Imeh3Wk4VMkuB3C4UQ+0/PVlNTkcWx9/5wwd+p3EnEbtvbZUhYIHgVBm+0FKEc22yrXBEc0N85fT/r4A== + +"@cucumber/message-streams@4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@cucumber/message-streams/-/message-streams-4.0.1.tgz#a5339d3504594bb2edb5732aaae94dddb24d0970" + integrity sha512-Kxap9uP5jD8tHUZVjTWgzxemi/0uOsbGjd4LBOSxcJoOCRbESFwemUzilJuzNTB8pcTQUh8D5oudUyxfkJOKmA== + +"@cucumber/messages@20.0.0": + version "20.0.0" + resolved "https://registry.yarnpkg.com/@cucumber/messages/-/messages-20.0.0.tgz#494d30bd2880b04dcb3a958f8c0ec5638b7b3596" + integrity sha512-JFrFwuhxsbig0afaViNhuzoQyC+GQzlI7m+rX+lSiDGV13K3sJzMmHjkbCiNOgoRlKAMwIGR9TRMH0xj9/My0w== + dependencies: + "@types/uuid" "8.3.4" + class-transformer "0.5.1" + reflect-metadata "0.1.13" + uuid "9.0.0" + +"@cucumber/messages@^19.1.4": + version "19.1.4" + resolved "https://registry.yarnpkg.com/@cucumber/messages/-/messages-19.1.4.tgz#5cefc47cac3004c0bc38d42933042ec248bb747c" + integrity sha512-Pksl0pnDz2l1+L5Ug85NlG6LWrrklN9qkMxN5Mv+1XZ3T6u580dnE6mVaxjJRdcOq4tR17Pc0RqIDZMyVY1FlA== + dependencies: + "@types/uuid" "8.3.4" + class-transformer "0.5.1" + reflect-metadata "0.1.13" + uuid "9.0.0" + +"@cucumber/tag-expressions@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@cucumber/tag-expressions/-/tag-expressions-4.1.0.tgz#9a91b0e0dd2f2ba703e3038c52b49b9ac06c2c6f" + integrity sha512-chTnjxV3vryL75N90wJIMdMafXmZoO2JgNJLYpsfcALL2/IQrRiny3vM9DgD5RDCSt1LNloMtb7rGey9YWxCsA== + "@jridgewell/gen-mapping@^0.3.0": version "0.3.2" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" @@ -71,6 +199,21 @@ dependencies: playwright "1.56.1" +"@teppeis/multimaps@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@teppeis/multimaps/-/multimaps-2.0.0.tgz#2114ee964b702f9777d0e07899087ad9cd89a0de" + integrity sha512-TL1adzq1HdxUf9WYduLcQ/DNGYiz71U31QRgbnr0Ef1cPyOUOsBojxHVWpFeOSUucB6Lrs0LxFRA14ntgtkc9w== + +"@types/lodash@^4.14.175": + version "4.17.24" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.24.tgz#4ae334fc62c0e915ca8ed8e35dcc6d4eeb29215f" + integrity sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ== + +"@types/uuid@8.3.4": + version "8.3.4" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" + integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== + acorn@^8.5.0: version "8.8.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" @@ -83,11 +226,33 @@ ansi-colors@^1.0.1: dependencies: ansi-wrap "^0.1.0" +ansi-regex@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" + integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + ansi-wrap@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" integrity sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw== +any-promise@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -98,6 +263,20 @@ arr-union@^3.1.0: resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q== +assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== + +assertion-error-formatter@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/assertion-error-formatter/-/assertion-error-formatter-3.0.0.tgz#be9c8825dee6a8a6c72183d915912d9b57d5d265" + integrity sha512-6YyAVLrEze0kQ7CmJfUgrLHb+Y7XghmL2Ie7ijVa2Y9ynP3LV+VDiwFk62Dn0qtqbmY0BT0ss6p1xxpiF2PYbQ== + dependencies: + diff "^4.0.1" + pad-right "^0.2.2" + repeat-string "^1.6.1" + assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" @@ -110,11 +289,41 @@ axios@^0.21.1: dependencies: follow-redirects "^1.14.0" +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +brace-expansion@^1.1.7: + version "1.1.14" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.14.tgz#d9de602370d91347cd9ddad1224d4fd701eb348b" + integrity sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +capital-case@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/capital-case/-/capital-case-1.0.4.tgz#9d130292353c9249f6b00fa5852bee38a717e669" + integrity sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + upper-case-first "^2.0.2" + +chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chance@^1.1.7: version "1.1.10" resolved "https://registry.yarnpkg.com/chance/-/chance-1.1.10.tgz#97aa50acaeb810ae293330a9e2b138ee3d49daf2" @@ -122,6 +331,20 @@ chance@^1.1.7: dependencies: gulp-uglify-es "^3.0.0" +class-transformer@0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.5.1.tgz#24147d5dffd2a6cea930a3250a677addf96ab336" + integrity sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw== + +cli-table3@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.3.tgz#61ab765aac156b52f222954ffc607a6f01dbeeb2" + integrity sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg== + dependencies: + string-width "^4.2.0" + optionalDependencies: + "@colors/colors" "1.5.0" + clone-buffer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" @@ -146,21 +369,160 @@ cloneable-readable@^1.0.0: process-nextick-args "^2.0.0" readable-stream "^2.3.5" +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +commander@9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-9.1.0.tgz#a6b263b2327f2e188c6402c42623327909f2dbec" + integrity sha512-i0/MaqBtdbnJ4XQs4Pmyb+oFQl+q0lsAmokVUH92SlSw4fkeAcG3bVon+Qt7hmtF+u3Het6o4VgrcY3qAoEB6w== + +commander@9.4.1: + version "9.4.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-9.4.1.tgz#d1dd8f2ce6faf93147295c0df13c7c21141cfbdd" + integrity sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw== + commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@^9.0.0: + version "9.5.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" + integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +core-util-is@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== + core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== +d@1, d@^1.0.1, d@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.2.tgz#2aefd554b81981e7dccf72d6842ae725cb17e5de" + integrity sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw== + dependencies: + es5-ext "^0.10.64" + type "^2.7.2" + +debug@^4.3.4: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + +diff@^4.0.1: + version "4.0.4" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.4.tgz#7a6dbfda325f25f07517e9b518f897c08332e07d" + integrity sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ== + dotenv@^8.0.0: version "8.6.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== +duration@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/duration/-/duration-0.2.2.tgz#ddf149bc3bc6901150fe9017111d016b3357f529" + integrity sha512-06kgtea+bGreF5eKYgI/36A6pLXggY7oR4p1pq4SmdFBn1ReOL5D8RhG64VrqfTTKNucqqtBAwEj8aB88mcqrg== + dependencies: + d "1" + es5-ext "~0.10.46" + +durations@^3.4.2: + version "3.4.2" + resolved "https://registry.yarnpkg.com/durations/-/durations-3.4.2.tgz#1de230454373cccfecab927de0bebae2295301db" + integrity sha512-V/lf7y33dGaypZZetVI1eu7BmvkbC4dItq12OElLRpKuaU5JxQstV2zHwLv8P7cNbQ+KL1WD80zMCTx5dNC4dg== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +error-stack-parser@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286" + integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ== + dependencies: + stackframe "^1.3.4" + +es5-ext@^0.10.35, es5-ext@^0.10.62, es5-ext@^0.10.64, es5-ext@~0.10.14, es5-ext@~0.10.46: + version "0.10.64" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.64.tgz#12e4ffb48f1ba2ea777f1fcdd1918ef73ea21714" + integrity sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg== + dependencies: + es6-iterator "^2.0.3" + es6-symbol "^3.1.3" + esniff "^2.0.1" + next-tick "^1.1.0" + +es6-iterator@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-symbol@^3.1.1, es6-symbol@^3.1.3: + version "3.1.4" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.4.tgz#f4e7d28013770b4208ecbf3e0bf14d3bcb557b8c" + integrity sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg== + dependencies: + d "^1.0.2" + ext "^1.7.0" + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +esniff@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308" + integrity sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg== + dependencies: + d "^1.0.1" + es5-ext "^0.10.62" + event-emitter "^0.3.5" + type "^2.7.2" + +event-emitter@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== + dependencies: + d "1" + es5-ext "~0.10.14" + +ext@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" + integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== + dependencies: + type "^2.7.2" + extend-shallow@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" @@ -169,11 +531,23 @@ extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" +extsprintf@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" + integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== + faker@^5.5.3: version "5.5.3" resolved "https://registry.yarnpkg.com/faker/-/faker-5.5.3.tgz#c57974ee484431b25205c2c8dc09fda861e51e0e" integrity sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g== +figures@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + fn-name@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/fn-name/-/fn-name-3.0.0.tgz#0596707f635929634d791f452309ab41558e3c5c" @@ -184,11 +558,35 @@ follow-redirects@^1.14.0: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + fsevents@2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== +glob@^7.1.6: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global-dirs@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.1.tgz#0c488971f066baceda21447aecb1a8b911d22485" + integrity sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA== + dependencies: + ini "2.0.0" + gulp-uglify-es@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/gulp-uglify-es/-/gulp-uglify-es-3.0.0.tgz#00466e0e3b0486057c552b8b0d3e326791f2f832" @@ -200,11 +598,41 @@ gulp-uglify-es@^3.0.0: vinyl "^2.2.1" vinyl-sourcemaps-apply "^0.2.1" -inherits@^2.0.1, inherits@~2.0.3: +has-ansi@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-4.0.1.tgz#f216a8c8d7b129e490dc15f4a62cc1cdb9603ce8" + integrity sha512-Qr4RtTm30xvEdqUXbSBVWDu+PrTokJOwe/FU+VdfJPk+MXAPoeOzKpRyrDTnZIJwAkQ4oBLTU53nu0HrkF/Z2A== + dependencies: + ansi-regex "^4.1.0" + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +ini@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" + integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== + is-extendable@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" @@ -212,6 +640,24 @@ is-extendable@^1.0.1: dependencies: is-plain-object "^2.0.4" +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-installed-globally@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" + integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== + dependencies: + global-dirs "^3.0.0" + is-path-inside "^3.0.2" + +is-path-inside@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -219,6 +665,11 @@ is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -229,21 +680,120 @@ isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== +knuth-shuffle-seeded@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/knuth-shuffle-seeded/-/knuth-shuffle-seeded-1.0.6.tgz#01f1b65733aa7540ee08d8b0174164d22081e4e1" + integrity sha512-9pFH0SplrfyKyojCLxZfMcvkhf5hH0d+UwR9nTVJ/DDQJGuzcXjTwB7TP7sDfehSudlGGaOLblmEWqv04ERVWg== + dependencies: + seed-random "~2.2.0" + lodash-es@^4.17.11: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== +lodash-es@^4.17.21: + version "4.18.1" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.18.1.tgz#b962eeb80d9d983a900bf342961fb7418ca10b1d" + integrity sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.mergewith@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" + integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== + lodash@^4.17.15, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +minimatch@^3.1.1: + version "3.1.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.5.tgz#580c88f8d5445f2bd6aa8f3cadefa0de79fbd69e" + integrity sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w== + dependencies: + brace-expansion "^1.1.7" + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +mz@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + +nanoclone@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/nanoclone/-/nanoclone-0.2.1.tgz#dd4090f8f1a110d26bb32c49ed2f5b9235209ed4" + integrity sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA== + +next-tick@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== + +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" + o-stream@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/o-stream/-/o-stream-0.3.0.tgz#204d27bc3fb395164507d79b381e91752e8daedc" integrity sha512-gbzl6qCJZ609x/M2t25HqCYQagFzWYCtQ84jcuObGr+V8D1Am4EVubkF4J+XFs6ukfiv96vNeiBb8FrbbMZYiQ== +object-assign@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +pad-right@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/pad-right/-/pad-right-0.2.2.tgz#6fbc924045d244f2a2a244503060d3bfc6009774" + integrity sha512-4cy8M95ioIGolCoMmm2cMntGR1lPLEbOMzOKu8bzjuJP6JpzEMQcDHmh7hHLYGgob+nKe1YHFMaG4V59HQa89g== + dependencies: + repeat-string "^1.5.2" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + playwright-core@1.56.1: version "1.56.1" resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.56.1.tgz#24a66481e5cd33a045632230aa2c4f0cb6b1db3d" @@ -278,11 +828,21 @@ process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== +progress@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + property-expr@^2.0.2: version "2.0.5" resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.5.tgz#278bdb15308ae16af3e3b9640024524f4dc02cb4" integrity sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA== +property-expr@^2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.6.tgz#f77bc00d5928a6c748414ad12882e83f24aec1e8" + integrity sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA== + readable-stream@^2.3.5: version "2.3.8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" @@ -296,27 +856,73 @@ readable-stream@^2.3.5: string_decoder "~1.1.1" util-deprecate "~1.0.1" +reflect-metadata@0.1.13: + version "0.1.13" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" + integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== + regenerator-runtime@^0.13.11: version "0.13.11" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== +regexp-match-indices@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regexp-match-indices/-/regexp-match-indices-1.0.2.tgz#cf20054a6f7d5b3e116a701a7b00f82889d10da6" + integrity sha512-DwZuAkt8NF5mKwGGER1EGh2PRqyvhRhhLviH+R8y8dIuaQROlUfXjt4s9ZTXstIsSkptf06BSvwcEmmfheJJWQ== + dependencies: + regexp-tree "^0.1.11" + +regexp-tree@^0.1.11: + version "0.1.27" + resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.27.tgz#2198f0ef54518ffa743fe74d983b56ffd631b6cd" + integrity sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA== + remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== +repeat-string@^1.5.2, repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== + replace-ext@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.1.tgz#2d6d996d04a15855d967443631dd5f77825b016a" integrity sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw== +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-pkg/-/resolve-pkg-2.0.0.tgz#ac06991418a7623edc119084edc98b0e6bf05a41" + integrity sha512-+1lzwXehGCXSeryaISr6WujZzowloigEofRB+dj75y9RRa/obVcYgbHJd53tdYw8pvZj8GojXaaENws8Ktw/hQ== + dependencies: + resolve-from "^5.0.0" + safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -source-map-support@~0.5.20: +seed-random@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/seed-random/-/seed-random-2.2.0.tgz#2a9b19e250a817099231a5b99a4daf80b7fbed54" + integrity sha512-34EQV6AAHQGhoc0tn/96a9Fsi6v2xdqe/dMUwljGRaFOzR3EgRmECvD0O8vi8X+/uQ50LGHfkNu/Eue5TPKZkQ== + +semver@7.3.8: + version "7.3.8" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" + integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== + dependencies: + lru-cache "^6.0.0" + +source-map-support@0.5.21, source-map-support@^0.5.21, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -334,6 +940,25 @@ source-map@^0.6.0: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +stackframe@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" + integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== + +string-argv@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" + integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== + +string-width@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -341,6 +966,27 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" +strip-ansi@6.0.1, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + synchronous-promise@^2.0.13: version "2.0.17" resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.17.tgz#38901319632f946c982152586f2caf8ddc25c032" @@ -356,11 +1002,52 @@ terser@^5.7.1: commander "^2.20.0" source-map-support "~0.5.20" +thenify-all@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.1" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" + integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== + dependencies: + any-promise "^1.0.0" + +tmp@^0.2.1: + version "0.2.5" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.5.tgz#b06bcd23f0f3c8357b426891726d16015abfd8f8" + integrity sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow== + toposort@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330" integrity sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg== +tslib@^2.0.3: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + +type@^2.7.2: + version "2.7.3" + resolved "https://registry.yarnpkg.com/type/-/type-2.7.3.tgz#436981652129285cc3ba94f392886c2637ea0486" + integrity sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ== + +upper-case-first@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-2.0.2.tgz#992c3273f882abd19d1e02894cc147117f844324" + integrity sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg== + dependencies: + tslib "^2.0.3" + +util-arity@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/util-arity/-/util-arity-1.1.0.tgz#59d01af1fdb3fede0ac4e632b0ab5f6ce97c9330" + integrity sha512-kkyIsXKwemfSy8ZEoaIz06ApApnWsk5hQO0vLjZS6UkBiGiW++Jsyb8vSBoc0WKlffGoGs5yYy/j5pp8zckrFA== + util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -371,6 +1058,20 @@ uuid@8.3.2, uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +uuid@9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" + integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== + +verror@^1.10.0: + version "1.10.1" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.1.tgz#4bf09eeccf4563b109ed4b3d458380c972b0cdeb" + integrity sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg== + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + vinyl-sourcemaps-apply@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz#ab6549d61d172c2b1b87be5c508d239c8ef87705" @@ -390,6 +1091,21 @@ vinyl@^2.2.1: remove-trailing-separator "^1.0.1" replace-ext "^1.0.0" +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +xmlbuilder@^15.1.1: + version "15.1.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5" + integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + yup@^0.29.3: version "0.29.3" resolved "https://registry.yarnpkg.com/yup/-/yup-0.29.3.tgz#69a30fd3f1c19f5d9e31b1cf1c2b851ce8045fea" @@ -402,3 +1118,16 @@ yup@^0.29.3: property-expr "^2.0.2" synchronous-promise "^2.0.13" toposort "^2.0.2" + +yup@^0.32.11: + version "0.32.11" + resolved "https://registry.yarnpkg.com/yup/-/yup-0.32.11.tgz#d67fb83eefa4698607982e63f7ca4c5ed3cf18c5" + integrity sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg== + dependencies: + "@babel/runtime" "^7.15.4" + "@types/lodash" "^4.14.175" + lodash "^4.17.21" + lodash-es "^4.17.21" + nanoclone "^0.2.1" + property-expr "^2.0.4" + toposort "^2.0.2" diff --git a/yarn.lock b/yarn.lock index 02df1c88..9bfbc160 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1388,6 +1388,11 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + "@comicrelief/data-models@^1.15.4": version "1.71.1" resolved "https://registry.yarnpkg.com/@comicrelief/data-models/-/data-models-1.71.1.tgz#3861e94c8fe9180c3ee0e5c86605073ce5df4778" @@ -1439,6 +1444,124 @@ resolved "https://registry.yarnpkg.com/@comicrelief/test-utils/-/test-utils-1.5.15.tgz#48ee30ccfa44d220dabce8dd7e9bb43c8e043814" integrity sha512-XsShlQcGzHDv6FVlJLvWGDPdY4mgqFNWdw+dXlGFXCCiC2WCpVQzVdzdInOTfvCZZXLAHDTT+RaF+RZlj3VVRQ== +"@cucumber/ci-environment@9.1.0": + version "9.1.0" + resolved "https://registry.yarnpkg.com/@cucumber/ci-environment/-/ci-environment-9.1.0.tgz#6d868141c7cfd616931f14723e122a1069401998" + integrity sha512-jdnF6APXP3GawMue8kdMxhu6TBhyRUO4KDRxTowf06NtclLjIw2Ybpo9IcIOMvE8kHukvJyM00uxWX+CfS7JgQ== + +"@cucumber/cucumber-expressions@16.1.0": + version "16.1.0" + resolved "https://registry.yarnpkg.com/@cucumber/cucumber-expressions/-/cucumber-expressions-16.1.0.tgz#2a2538b775e83b84a275ea10f618242717937968" + integrity sha512-Q/tKDNje9RrcOXF2TO2NwTW92rzk+RwKkhYYKLxQT26Co8Qbjom0Cz02HsCMA2wjJ8dw6/d2IbWgiOay9RQA+w== + dependencies: + regexp-match-indices "1.0.2" + +"@cucumber/cucumber@8.9.1": + version "8.9.1" + resolved "https://registry.yarnpkg.com/@cucumber/cucumber/-/cucumber-8.9.1.tgz#6b3bb74e5811d69aee85eaac0298218cb38ab6c7" + integrity sha512-Ir0yloCGu25mnllHunOAaOVXIKrRSE0ICF1FleoJzeupgYWK/x8o6hjOGRJf1Ui17GoRs1CfkP44/K29lmpMtA== + dependencies: + "@cucumber/ci-environment" "9.1.0" + "@cucumber/cucumber-expressions" "16.1.0" + "@cucumber/gherkin" "25.0.2" + "@cucumber/gherkin-streams" "5.0.1" + "@cucumber/gherkin-utils" "8.0.2" + "@cucumber/html-formatter" "20.2.0" + "@cucumber/message-streams" "4.0.1" + "@cucumber/messages" "20.0.0" + "@cucumber/tag-expressions" "4.1.0" + assertion-error-formatter "^3.0.0" + capital-case "^1.0.4" + chalk "^4.1.2" + cli-table3 "0.6.3" + commander "^9.0.0" + debug "^4.3.4" + duration "^0.2.2" + durations "^3.4.2" + error-stack-parser "^2.1.4" + figures "^3.2.0" + glob "^7.1.6" + has-ansi "^4.0.1" + indent-string "^4.0.0" + is-installed-globally "^0.4.0" + is-stream "^2.0.0" + knuth-shuffle-seeded "^1.0.6" + lodash.merge "^4.6.2" + lodash.mergewith "^4.6.2" + mz "^2.7.0" + progress "^2.0.3" + resolve-pkg "^2.0.0" + semver "7.3.8" + string-argv "^0.3.1" + strip-ansi "6.0.1" + supports-color "^8.1.1" + tmp "^0.2.1" + util-arity "^1.1.0" + verror "^1.10.0" + xmlbuilder "^15.1.1" + yup "^0.32.11" + +"@cucumber/gherkin-streams@5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@cucumber/gherkin-streams/-/gherkin-streams-5.0.1.tgz#8c2142d295cd05644456be7282b4bd756c95c4cd" + integrity sha512-/7VkIE/ASxIP/jd4Crlp4JHXqdNFxPGQokqWqsaCCiqBiu5qHoKMxcWNlp9njVL/n9yN4S08OmY3ZR8uC5x74Q== + dependencies: + commander "9.1.0" + source-map-support "0.5.21" + +"@cucumber/gherkin-utils@8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@cucumber/gherkin-utils/-/gherkin-utils-8.0.2.tgz#deae231f84e91f120501d22187c66d36e6c6b59f" + integrity sha512-aQlziN3r3cTwprEDbLEcFoMRQajb9DTOu2OZZp5xkuNz6bjSTowSY90lHUD2pWT7jhEEckZRIREnk7MAwC2d1A== + dependencies: + "@cucumber/gherkin" "^25.0.0" + "@cucumber/messages" "^19.1.4" + "@teppeis/multimaps" "2.0.0" + commander "9.4.1" + source-map-support "^0.5.21" + +"@cucumber/gherkin@25.0.2", "@cucumber/gherkin@^25.0.0": + version "25.0.2" + resolved "https://registry.yarnpkg.com/@cucumber/gherkin/-/gherkin-25.0.2.tgz#e430879f01978d1f9e7a7aa0563031a3a36022e7" + integrity sha512-EdsrR33Y5GjuOoe2Kq5Y9DYwgNRtUD32H4y2hCrT6+AWo7ibUQu7H+oiWTgfVhwbkHsZmksxHSxXz/AwqqyCRQ== + dependencies: + "@cucumber/messages" "^19.1.4" + +"@cucumber/html-formatter@20.2.0": + version "20.2.0" + resolved "https://registry.yarnpkg.com/@cucumber/html-formatter/-/html-formatter-20.2.0.tgz#20857efec721fbc5d64cc31b0107575db1e58651" + integrity sha512-apcxS5Imeh3Wk4VMkuB3C4UQ+0/PVlNTkcWx9/5wwd+p3EnEbtvbZUhYIHgVBm+0FKEc22yrXBEc0N85fT/r4A== + +"@cucumber/message-streams@4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@cucumber/message-streams/-/message-streams-4.0.1.tgz#a5339d3504594bb2edb5732aaae94dddb24d0970" + integrity sha512-Kxap9uP5jD8tHUZVjTWgzxemi/0uOsbGjd4LBOSxcJoOCRbESFwemUzilJuzNTB8pcTQUh8D5oudUyxfkJOKmA== + +"@cucumber/messages@20.0.0": + version "20.0.0" + resolved "https://registry.yarnpkg.com/@cucumber/messages/-/messages-20.0.0.tgz#494d30bd2880b04dcb3a958f8c0ec5638b7b3596" + integrity sha512-JFrFwuhxsbig0afaViNhuzoQyC+GQzlI7m+rX+lSiDGV13K3sJzMmHjkbCiNOgoRlKAMwIGR9TRMH0xj9/My0w== + dependencies: + "@types/uuid" "8.3.4" + class-transformer "0.5.1" + reflect-metadata "0.1.13" + uuid "9.0.0" + +"@cucumber/messages@^19.1.4": + version "19.1.4" + resolved "https://registry.yarnpkg.com/@cucumber/messages/-/messages-19.1.4.tgz#5cefc47cac3004c0bc38d42933042ec248bb747c" + integrity sha512-Pksl0pnDz2l1+L5Ug85NlG6LWrrklN9qkMxN5Mv+1XZ3T6u580dnE6mVaxjJRdcOq4tR17Pc0RqIDZMyVY1FlA== + dependencies: + "@types/uuid" "8.3.4" + class-transformer "0.5.1" + reflect-metadata "0.1.13" + uuid "9.0.0" + +"@cucumber/tag-expressions@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@cucumber/tag-expressions/-/tag-expressions-4.1.0.tgz#9a91b0e0dd2f2ba703e3038c52b49b9ac06c2c6f" + integrity sha512-chTnjxV3vryL75N90wJIMdMafXmZoO2JgNJLYpsfcALL2/IQrRiny3vM9DgD5RDCSt1LNloMtb7rGey9YWxCsA== + "@emotion/cache@^10.0.27": version "10.0.29" resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-10.0.29.tgz#87e7e64f412c060102d589fe7c6dc042e6f9d1e0" @@ -2253,6 +2376,11 @@ "@svgr/plugin-svgo" "^4.3.1" loader-utils "^1.2.3" +"@teppeis/multimaps@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@teppeis/multimaps/-/multimaps-2.0.0.tgz#2114ee964b702f9777d0e07899087ad9cd89a0de" + integrity sha512-TL1adzq1HdxUf9WYduLcQ/DNGYiz71U31QRgbnr0Ef1cPyOUOsBojxHVWpFeOSUucB6Lrs0LxFRA14ntgtkc9w== + "@tootallnate/once@1": version "1.1.2" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" @@ -2451,6 +2579,11 @@ dependencies: source-map "^0.6.1" +"@types/uuid@8.3.4": + version "8.3.4" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" + integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== + "@types/webpack-env@^1.15.0": version "1.18.1" resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.18.1.tgz#49699bb508961e14a3bfb68c78cd87b296889d1d" @@ -3265,6 +3398,15 @@ assert@^1.1.1: object-assign "^4.1.1" util "0.10.3" +assertion-error-formatter@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/assertion-error-formatter/-/assertion-error-formatter-3.0.0.tgz#be9c8825dee6a8a6c72183d915912d9b57d5d265" + integrity sha512-6YyAVLrEze0kQ7CmJfUgrLHb+Y7XghmL2Ie7ijVa2Y9ynP3LV+VDiwFk62Dn0qtqbmY0BT0ss6p1xxpiF2PYbQ== + dependencies: + diff "^4.0.1" + pad-right "^0.2.2" + repeat-string "^1.6.1" + assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" @@ -4832,6 +4974,15 @@ caniuse-lite@^1.0.30001629: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001632.tgz#964207b7cba5851701afb4c8afaf1448db3884b6" integrity sha512-udx3o7yHJfUxMLkGohMlVHCvFvWmirKh9JAH/d7WOLPetlH+LTL5cocMZ0t7oZx/mdlOWXti97xLZWc8uURRHg== +capital-case@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/capital-case/-/capital-case-1.0.4.tgz#9d130292353c9249f6b00fa5852bee38a717e669" + integrity sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + upper-case-first "^2.0.2" + capture-stack-trace@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.2.tgz#1c43f6b059d4249e7f3f8724f15f048b927d3a8a" @@ -5046,6 +5197,11 @@ clap@^1.0.9: dependencies: chalk "^1.1.3" +class-transformer@0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.5.1.tgz#24147d5dffd2a6cea930a3250a677addf96ab336" + integrity sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw== + class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" @@ -5107,6 +5263,15 @@ cli-table3@0.5.1: optionalDependencies: colors "^1.1.2" +cli-table3@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.3.tgz#61ab765aac156b52f222954ffc607a6f01dbeeb2" + integrity sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg== + dependencies: + string-width "^4.2.0" + optionalDependencies: + "@colors/colors" "1.5.0" + cli-width@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" @@ -5288,6 +5453,16 @@ comma-separated-tokens@^1.0.0: resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== +commander@9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-9.1.0.tgz#a6b263b2327f2e188c6402c42623327909f2dbec" + integrity sha512-i0/MaqBtdbnJ4XQs4Pmyb+oFQl+q0lsAmokVUH92SlSw4fkeAcG3bVon+Qt7hmtF+u3Het6o4VgrcY3qAoEB6w== + +commander@9.4.1: + version "9.4.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-9.4.1.tgz#d1dd8f2ce6faf93147295c0df13c7c21141cfbdd" + integrity sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw== + commander@^2.15.1, commander@^2.19.0, commander@^2.20.0, commander@^2.8.1: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -5303,6 +5478,11 @@ commander@^7.2.0: resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== +commander@^9.0.0: + version "9.5.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" + integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== + commander@~2.13.0: version "2.13.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" @@ -6036,6 +6216,14 @@ d3-voronoi@^1.1.4: resolved "https://registry.yarnpkg.com/d3-voronoi/-/d3-voronoi-1.1.4.tgz#dd3c78d7653d2bb359284ae478645d95944c8297" integrity sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg== +d@1, d@^1.0.1, d@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.2.tgz#2aefd554b81981e7dccf72d6842ae725cb17e5de" + integrity sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw== + dependencies: + es5-ext "^0.10.64" + type "^2.7.2" + damerau-levenshtein@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" @@ -6078,6 +6266,13 @@ debug@^3.0.0, debug@^3.1.0, debug@^3.2.5, debug@^3.2.7: dependencies: ms "^2.1.1" +debug@^4.3.4: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + decamelize-keys@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz#04a2d523b2f18d80d0158a43b895d56dff8d19d8" @@ -6330,6 +6525,11 @@ detect-port@^1.3.0: address "^1.0.1" debug "4" +diff@^4.0.1: + version "4.0.4" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.4.tgz#7a6dbfda325f25f07517e9b518f897c08332e07d" + integrity sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ== + diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" @@ -6618,6 +6818,19 @@ duplexify@^3.4.2, duplexify@^3.6.0: readable-stream "^2.0.0" stream-shift "^1.0.0" +duration@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/duration/-/duration-0.2.2.tgz#ddf149bc3bc6901150fe9017111d016b3357f529" + integrity sha512-06kgtea+bGreF5eKYgI/36A6pLXggY7oR4p1pq4SmdFBn1ReOL5D8RhG64VrqfTTKNucqqtBAwEj8aB88mcqrg== + dependencies: + d "1" + es5-ext "~0.10.46" + +durations@^3.4.2: + version "3.4.2" + resolved "https://registry.yarnpkg.com/durations/-/durations-3.4.2.tgz#1de230454373cccfecab927de0bebae2295301db" + integrity sha512-V/lf7y33dGaypZZetVI1eu7BmvkbC4dItq12OElLRpKuaU5JxQstV2zHwLv8P7cNbQ+KL1WD80zMCTx5dNC4dg== + ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -6772,6 +6985,13 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" +error-stack-parser@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286" + integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ== + dependencies: + stackframe "^1.3.4" + es-abstract@^1.17.2, es-abstract@^1.19.0, es-abstract@^1.20.4, es-abstract@^1.21.2: version "1.22.1" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.1.tgz#8b4e5fc5cefd7f1660f0f8e1a52900dfbc9d9ccc" @@ -6862,6 +7082,16 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +es5-ext@^0.10.35, es5-ext@^0.10.62, es5-ext@^0.10.64, es5-ext@~0.10.14, es5-ext@~0.10.46: + version "0.10.64" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.64.tgz#12e4ffb48f1ba2ea777f1fcdd1918ef73ea21714" + integrity sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg== + dependencies: + es6-iterator "^2.0.3" + es6-symbol "^3.1.3" + esniff "^2.0.1" + next-tick "^1.1.0" + es5-shim@^4.5.13: version "4.6.7" resolved "https://registry.yarnpkg.com/es5-shim/-/es5-shim-4.6.7.tgz#bc67ae0fc3dd520636e0a1601cc73b450ad3e955" @@ -6872,6 +7102,15 @@ es6-error@^4.1.1: resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== +es6-iterator@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + es6-promise@^4.0.5: version "4.2.8" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" @@ -6882,6 +7121,14 @@ es6-shim@^0.35.5: resolved "https://registry.yarnpkg.com/es6-shim/-/es6-shim-0.35.8.tgz#89216f6fbf8bacba3f897c8c0e814d2a41c05fb7" integrity sha512-Twf7I2v4/1tLoIXMT8HlqaBSS5H2wQTs2wx3MNYCI8K1R1/clXyCazrcVCPm/FuO9cyV8+leEaZOWD5C253NDg== +es6-symbol@^3.1.1, es6-symbol@^3.1.3: + version "3.1.4" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.4.tgz#f4e7d28013770b4208ecbf3e0bf14d3bcb557b8c" + integrity sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg== + dependencies: + d "^1.0.2" + ext "^1.7.0" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -7120,6 +7367,16 @@ eslint@^7.8.1: text-table "^0.2.0" v8-compile-cache "^2.0.3" +esniff@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308" + integrity sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg== + dependencies: + d "^1.0.1" + es5-ext "^0.10.62" + event-emitter "^0.3.5" + type "^2.7.2" + espree@^7.3.0, espree@^7.3.1: version "7.3.1" resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" @@ -7182,6 +7439,14 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== +event-emitter@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== + dependencies: + d "1" + es5-ext "~0.10.14" + event-stream@=3.3.4: version "3.3.4" resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" @@ -7365,6 +7630,13 @@ ext-name@^5.0.0: ext-list "^2.0.0" sort-keys-length "^1.0.0" +ext@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" + integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== + dependencies: + type "^2.7.2" + extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -7554,7 +7826,7 @@ figures@^2.0.0: dependencies: escape-string-regexp "^1.0.5" -figures@^3.0.0: +figures@^3.0.0, figures@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== @@ -8291,6 +8563,13 @@ global-dirs@^0.1.0: dependencies: ini "^1.3.4" +global-dirs@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.1.tgz#0c488971f066baceda21447aecb1a8b911d22485" + integrity sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA== + dependencies: + ini "2.0.0" + global-modules@2.0.0, global-modules@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" @@ -8566,6 +8845,13 @@ has-ansi@^2.0.0: dependencies: ansi-regex "^2.0.0" +has-ansi@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-4.0.1.tgz#f216a8c8d7b129e490dc15f4a62cc1cdb9603ce8" + integrity sha512-Qr4RtTm30xvEdqUXbSBVWDu+PrTokJOwe/FU+VdfJPk+MXAPoeOzKpRyrDTnZIJwAkQ4oBLTU53nu0HrkF/Z2A== + dependencies: + ansi-regex "^4.1.0" + has-bigints@^1.0.1, has-bigints@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" @@ -9215,6 +9501,11 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== +ini@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" + integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== + ini@^1.3.4, ini@^1.3.5, ini@~1.3.0: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" @@ -9607,6 +9898,14 @@ is-installed-globally@^0.1.0: global-dirs "^0.1.0" is-path-inside "^1.0.0" +is-installed-globally@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" + integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== + dependencies: + global-dirs "^3.0.0" + is-path-inside "^3.0.2" + is-jpg@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-jpg/-/is-jpg-2.0.0.tgz#2e1997fa6e9166eaac0242daae443403e4ef1d97" @@ -9704,6 +10003,11 @@ is-path-inside@^2.1.0: dependencies: path-is-inside "^1.0.2" +is-path-inside@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" @@ -10221,6 +10525,13 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +knuth-shuffle-seeded@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/knuth-shuffle-seeded/-/knuth-shuffle-seeded-1.0.6.tgz#01f1b65733aa7540ee08d8b0174164d22081e4e1" + integrity sha512-9pFH0SplrfyKyojCLxZfMcvkhf5hH0d+UwR9nTVJ/DDQJGuzcXjTwB7TP7sDfehSudlGGaOLblmEWqv04ERVWg== + dependencies: + seed-random "~2.2.0" + language-subtag-registry@~0.3.2: version "0.3.22" resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz#2e1500861b2e457eba7e7ae86877cbd08fa1fd1d" @@ -10427,6 +10738,11 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.mergewith@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" + integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== + lodash.tail@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.tail/-/lodash.tail-4.1.1.tgz#d2333a36d9e7717c8ad2f7cacafec7c32b444664" @@ -11160,7 +11476,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3, ms@^2.0.0, ms@^2.1.1: +ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -11188,7 +11504,7 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -mz@^2.6.0: +mz@^2.6.0, mz@^2.7.0: version "2.7.0" resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== @@ -11249,6 +11565,11 @@ nested-object-assign@^1.0.1: resolved "https://registry.yarnpkg.com/nested-object-assign/-/nested-object-assign-1.0.4.tgz#c9db56078eb6043960fdb6ba918a5122a06ccac4" integrity sha512-FlZ7oN9ICt+fbcJ4ag2IsALIcalfE/E16ttdSA8peBiHJI+oEKdOcafqDnUbeUe5NwWGn/m9zZGO9qrAGzfesg== +next-tick@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -11945,6 +12266,13 @@ package-json@^4.0.0: registry-url "^3.0.3" semver "^5.1.0" +pad-right@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/pad-right/-/pad-right-0.2.2.tgz#6fbc924045d244f2a2a244503060d3bfc6009774" + integrity sha512-4cy8M95ioIGolCoMmm2cMntGR1lPLEbOMzOKu8bzjuJP6JpzEMQcDHmh7hHLYGgob+nKe1YHFMaG4V59HQa89g== + dependencies: + repeat-string "^1.5.2" + pako@~1.0.5: version "1.0.11" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" @@ -12781,7 +13109,7 @@ process@^0.11.10: resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== -progress@^2.0.0: +progress@^2.0.0, progress@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== @@ -13691,6 +14019,11 @@ reduce-function-call@^1.0.1: dependencies: balanced-match "^1.0.0" +reflect-metadata@0.1.13: + version "0.1.13" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" + integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== + refractor@^2.4.1: version "2.10.1" resolved "https://registry.yarnpkg.com/refractor/-/refractor-2.10.1.tgz#166c32f114ed16fd96190ad21d5193d3afc7d34e" @@ -13756,6 +14089,18 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regexp-match-indices@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regexp-match-indices/-/regexp-match-indices-1.0.2.tgz#cf20054a6f7d5b3e116a701a7b00f82889d10da6" + integrity sha512-DwZuAkt8NF5mKwGGER1EGh2PRqyvhRhhLviH+R8y8dIuaQROlUfXjt4s9ZTXstIsSkptf06BSvwcEmmfheJJWQ== + dependencies: + regexp-tree "^0.1.11" + +regexp-tree@^0.1.11: + version "0.1.27" + resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.27.tgz#2198f0ef54518ffa743fe74d983b56ffd631b6cd" + integrity sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA== + regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.4.3, regexp.prototype.flags@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" @@ -13872,7 +14217,7 @@ repeat-element@^1.1.2: resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== -repeat-string@^1.5.4, repeat-string@^1.6.1: +repeat-string@^1.5.2, repeat-string@^1.5.4, repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== @@ -13977,6 +14322,13 @@ resolve-pathname@^3.0.0: resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng== +resolve-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-pkg/-/resolve-pkg-2.0.0.tgz#ac06991418a7623edc119084edc98b0e6bf05a41" + integrity sha512-+1lzwXehGCXSeryaISr6WujZzowloigEofRB+dj75y9RRa/obVcYgbHJd53tdYw8pvZj8GojXaaENws8Ktw/hQ== + dependencies: + resolve-from "^5.0.0" + resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -14249,6 +14601,11 @@ scss-tokenizer@^0.2.3: js-base64 "^2.1.8" source-map "^0.4.2" +seed-random@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/seed-random/-/seed-random-2.2.0.tgz#2a9b19e250a817099231a5b99a4daf80b7fbed54" + integrity sha512-34EQV6AAHQGhoc0tn/96a9Fsi6v2xdqe/dMUwljGRaFOzR3EgRmECvD0O8vi8X+/uQ50LGHfkNu/Eue5TPKZkQ== + seek-bzip@^1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.6.tgz#35c4171f55a680916b52a07859ecf3b5857f21c4" @@ -14297,6 +14654,13 @@ semver-truncate@^1.1.2: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== +semver@7.3.8: + version "7.3.8" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" + integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== + dependencies: + lru-cache "^6.0.0" + semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" @@ -14695,7 +15059,7 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@~0.5.12: +source-map-support@0.5.21, source-map-support@^0.5.21, source-map-support@~0.5.12: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -14866,6 +15230,11 @@ stable@^0.1.8: resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== +stackframe@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" + integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== + start-server-and-test@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/start-server-and-test/-/start-server-and-test-2.0.0.tgz#0644809d63036a8a001efb70582f3e37ebfdd33d" @@ -14966,6 +15335,11 @@ strict-uri-encode@^1.0.0: resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" integrity sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ== +string-argv@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" + integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== + string-natural-compare@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" @@ -15086,6 +15460,13 @@ strip-ansi@5.2.0, strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" @@ -15100,13 +15481,6 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" @@ -15221,6 +15595,13 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +supports-color@^8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" @@ -15575,6 +15956,11 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" +tmp@^0.2.1: + version "0.2.5" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.5.tgz#b06bcd23f0f3c8357b426891726d16015abfd8f8" + integrity sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow== + to-arraybuffer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" @@ -15803,6 +16189,11 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +type@^2.7.2: + version "2.7.3" + resolved "https://registry.yarnpkg.com/type/-/type-2.7.3.tgz#436981652129285cc3ba94f392886c2637ea0486" + integrity sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ== + typed-array-buffer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" @@ -16118,6 +16509,13 @@ update-notifier@^2.3.0: semver-diff "^2.0.0" xdg-basedir "^3.0.0" +upper-case-first@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-2.0.2.tgz#992c3273f882abd19d1e02894cc147117f844324" + integrity sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg== + dependencies: + tslib "^2.0.3" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -16213,6 +16611,11 @@ use@^3.1.0: resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== +util-arity@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/util-arity/-/util-arity-1.1.0.tgz#59d01af1fdb3fede0ac4e632b0ab5f6ce97c9330" + integrity sha512-kkyIsXKwemfSy8ZEoaIz06ApApnWsk5hQO0vLjZS6UkBiGiW++Jsyb8vSBoc0WKlffGoGs5yYy/j5pp8zckrFA== + util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -16273,6 +16676,11 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== +uuid@9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" + integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== + uuid@^3.0.1, uuid@^3.3.2: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" @@ -16334,6 +16742,15 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +verror@^1.10.0: + version "1.10.1" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.1.tgz#4bf09eeccf4563b109ed4b3d458380c972b0cdeb" + integrity sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg== + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + vfile-location@^2.0.0: version "2.0.6" resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-2.0.6.tgz#8a274f39411b8719ea5728802e10d9e0dff1519e" @@ -16779,6 +17196,11 @@ xml-name-validator@^4.0.0: resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== +xmlbuilder@^15.1.1: + version "15.1.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5" + integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== + xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"