From aa4c3f2d4085f5f752d95d7c6c194c1b256327a1 Mon Sep 17 00:00:00 2001 From: vatsala-glory <31056784+vatsala-glory@users.noreply.github.com> Date: Wed, 18 Dec 2024 13:08:09 +0000 Subject: [PATCH 1/2] Cost efficiency (#344) * added proxy for stg sandbox env * Poc/transactions e2e tests (#323) * add e2e-tests to transactions * add fixtures * update e2e specs --------- Co-authored-by: Telman Ahababov Co-authored-by: Telman Ahababov * cost-efficiency: configure mock e2e (#328) configure mock e2e * Feature/e2e for transaction (#333) * added configs first commit (#335) * added configs first commit * review comment * update error scenario (#341) * update error scenario * login and adding placeholder config * review comment fixes * Fix comment from Edward * Remove credential * revert * Move locator to page file * update lock * revert * Add sndbx error tests (#347) * update error scenario * login and adding placeholder config * review comment fixes * removed extra data folder - exports from index.ts * moving everything to single fixture * added inline comments * format fixes * fix tags and describe blocks * revert ci change * run stable sandbox tests * fix visual e2e configuration * inline comments * updated data files * use tags in transactions list error tests * added interface * build issue fixed * build issue fixed * fixing build issue * renamed proxy.mocks * tests using config to determine data (#357) Co-authored-by: Vatsala * removed snapshots * use env variables for env urls * moved fixture to lib (#361) * update e2e run readme * cost-efficiency: update data setting configuration (#362) update data setting format --------- Co-authored-by: Telman Ahababov Co-authored-by: Telman Ahababov Co-authored-by: Tony Ngo Co-authored-by: ngohungphuc Co-authored-by: Ilya Co-authored-by: Vatsala --- README.md | 17 ++++- .../config/ebp-sndbx.config.json | 11 +++ .../config/localhost.config.json | 1 + .../data/mocks-data/index.ts | 1 + .../mocks-data/transactions.mocks.api.data.ts | 20 ++++++ .../data/sandbox-api-data/index.ts | 1 + .../transactions.sandbox.api.data.ts | 16 +++++ .../fixtures/transactions.fixture.ts | 42 +++++++++++ .../page-objects/test-runner.ts | 39 +++++++++- apps/golden-sample-app-e2e/project.json | 45 +++++++++++- .../golden-sample-app-e2e/specs/login.spec.ts | 45 ------------ .../specs/mb-login.spec.ts | 49 +++++++++++++ .../specs/transaction-details.spec.ts | 4 ++ .../specs/transactions-list.spec.ts | 4 ++ .../specs/transactions.spec.ts | 4 +- apps/golden-sample-app-e2e/utils/read-file.ts | 11 +++ apps/golden-sample-app/project.json | 9 ++- apps/golden-sample-app/proxy.mocks.conf.js | 5 ++ apps/golden-sample-app/proxy.mocks.conf.json | 5 -- apps/golden-sample-app/proxy.stg-ebp.conf.js | 23 ++++++ .../src/app/app-routing.module.ts | 2 +- .../src/environments/environment.ts | 2 +- .../e2e-tests/fixture/index.ts | 1 + .../e2e-tests/fixture/transaction.ts | 21 ++++++ libs/transactions-journey/e2e-tests/index.ts | 9 +++ .../locators/transactions.locators.ts | 7 ++ .../e2e-tests/model/index.ts | 1 + .../e2e-tests/model/transaction.ts | 26 +++++++ .../e2e-tests/page-object/index.ts | 2 + .../page-object/transaction-details.page.ts | 33 +++++++++ .../page-object/transactions-list.page.ts | 40 +++++++++++ .../specs/transaction-details.spec.ts | 26 +++++++ .../e2e-tests/specs/transactions-list.spec.ts | 42 +++++++++++ package.json | 8 ++- playwright.config.ts | 6 +- playwright.localhost.config.ts | 71 +++++++++++++++++++ playwright.modelbank.config.ts | 64 +++++++++++++++++ test.model.ts | 11 +++ tsconfig.base.json | 3 + 39 files changed, 657 insertions(+), 70 deletions(-) create mode 100644 apps/golden-sample-app-e2e/config/ebp-sndbx.config.json create mode 100644 apps/golden-sample-app-e2e/config/localhost.config.json create mode 100644 apps/golden-sample-app-e2e/data/mocks-data/index.ts create mode 100644 apps/golden-sample-app-e2e/data/mocks-data/transactions.mocks.api.data.ts create mode 100644 apps/golden-sample-app-e2e/data/sandbox-api-data/index.ts create mode 100644 apps/golden-sample-app-e2e/data/sandbox-api-data/transactions.sandbox.api.data.ts create mode 100644 apps/golden-sample-app-e2e/fixtures/transactions.fixture.ts delete mode 100644 apps/golden-sample-app-e2e/specs/login.spec.ts create mode 100644 apps/golden-sample-app-e2e/specs/mb-login.spec.ts create mode 100644 apps/golden-sample-app-e2e/specs/transaction-details.spec.ts create mode 100644 apps/golden-sample-app-e2e/specs/transactions-list.spec.ts create mode 100644 apps/golden-sample-app-e2e/utils/read-file.ts create mode 100644 apps/golden-sample-app/proxy.mocks.conf.js delete mode 100644 apps/golden-sample-app/proxy.mocks.conf.json create mode 100644 apps/golden-sample-app/proxy.stg-ebp.conf.js create mode 100644 libs/transactions-journey/e2e-tests/fixture/index.ts create mode 100644 libs/transactions-journey/e2e-tests/fixture/transaction.ts create mode 100644 libs/transactions-journey/e2e-tests/index.ts create mode 100644 libs/transactions-journey/e2e-tests/locators/transactions.locators.ts create mode 100644 libs/transactions-journey/e2e-tests/model/index.ts create mode 100644 libs/transactions-journey/e2e-tests/model/transaction.ts create mode 100644 libs/transactions-journey/e2e-tests/page-object/index.ts create mode 100644 libs/transactions-journey/e2e-tests/page-object/transaction-details.page.ts create mode 100644 libs/transactions-journey/e2e-tests/page-object/transactions-list.page.ts create mode 100644 libs/transactions-journey/e2e-tests/specs/transaction-details.spec.ts create mode 100644 libs/transactions-journey/e2e-tests/specs/transactions-list.spec.ts create mode 100644 playwright.localhost.config.ts create mode 100644 playwright.modelbank.config.ts create mode 100644 test.model.ts diff --git a/README.md b/README.md index 248e4f631..77872327d 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,13 @@ This golden sample provides examples of the code structure, configuration, and b - [Generate an application](#generate-an-application) - [Generate a library](#generate-a-library) - [Load app on a development server](#load-app-on-a-development-server) + - [User credentials](#user-credentials) + - [Running the app with Mocks](#running-the-app-with-mocks) - [Code scaffolding](#code-scaffolding) - [Build](#build) - [Tests](#tests) + - [Running unit tests](#running-unit-tests) + - [Running end-to-end tests](#running-end-to-end-tests) - [Understand your workspace](#understand-your-workspace) - [Running with docker](#running-with-docker) - [Package as a runnable Docker container](#package-as-a-runnable-docker-container) @@ -140,15 +144,22 @@ To build the app to production, use the `--prod` flag. ## Tests -- Running unit tests +### Running unit tests Run `ng test my-app` to execute the unit tests via [Jest](https://jestjs.io). Run `nx affected:test` to execute the unit tests affected by a change. -- Running end-to-end tests +### Running end-to-end tests -Run `npx playwright test` +Run `npm run e2e` to run the default e2e tests suite that runs on the CI. + +Use one of the following commands to run a different set of tests: + +- `npm run e2e-test-mocks` - run all the tests against mocks data, +- `npm run e2e-test-sndbx-all` - run all the tests against sandbox env, +- `npm run e2e-test-sndbx-ci` - run sandbox CI tests suite, +- `npm run e2e-test-responsive` - run only visual mobile tests. ## Understand your workspace diff --git a/apps/golden-sample-app-e2e/config/ebp-sndbx.config.json b/apps/golden-sample-app-e2e/config/ebp-sndbx.config.json new file mode 100644 index 000000000..f5d80e402 --- /dev/null +++ b/apps/golden-sample-app-e2e/config/ebp-sndbx.config.json @@ -0,0 +1,11 @@ +{ + "headerKey": "x-sdbxaz-api-key", + "headerValue": "apisandbox-d0d8278f-5fc5-46fb-5fc5-d0b6e1cc8059", + "users": { + "userWithNoContext": { + "username": "sdbxaz-stg-criscross", + "password": "GQXTZdIOsXeqBtjc", + "fullName": "Christopher Cross" + } + } +} diff --git a/apps/golden-sample-app-e2e/config/localhost.config.json b/apps/golden-sample-app-e2e/config/localhost.config.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/apps/golden-sample-app-e2e/config/localhost.config.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/apps/golden-sample-app-e2e/data/mocks-data/index.ts b/apps/golden-sample-app-e2e/data/mocks-data/index.ts new file mode 100644 index 000000000..bfa6e1f6c --- /dev/null +++ b/apps/golden-sample-app-e2e/data/mocks-data/index.ts @@ -0,0 +1 @@ +export * from './transactions.mocks.api.data'; diff --git a/apps/golden-sample-app-e2e/data/mocks-data/transactions.mocks.api.data.ts b/apps/golden-sample-app-e2e/data/mocks-data/transactions.mocks.api.data.ts new file mode 100644 index 000000000..98272fd8e --- /dev/null +++ b/apps/golden-sample-app-e2e/data/mocks-data/transactions.mocks.api.data.ts @@ -0,0 +1,20 @@ +import { + TransactionListDataType, + TransactionDetailDataType, +} from '@backbase-gsa/transactions-journey/e2e-tests'; + +export const transactionListMockData: TransactionListDataType = { + size: 10, + searchExpectations: [ + { term: 'KLM', count: 7 }, + { term: 'cafe', count: 3 }, + ], +}; + +export const transactionDetailMocksData: TransactionDetailDataType = { + recipient: 'Hard Rock Cafe', + category: 'Alcohol & Bars', + description: 'Beer Bar Salt Lake', + status: 'BILLED', + id: '007jb5', +}; diff --git a/apps/golden-sample-app-e2e/data/sandbox-api-data/index.ts b/apps/golden-sample-app-e2e/data/sandbox-api-data/index.ts new file mode 100644 index 000000000..814389e72 --- /dev/null +++ b/apps/golden-sample-app-e2e/data/sandbox-api-data/index.ts @@ -0,0 +1 @@ +export * from './transactions.sandbox.api.data'; diff --git a/apps/golden-sample-app-e2e/data/sandbox-api-data/transactions.sandbox.api.data.ts b/apps/golden-sample-app-e2e/data/sandbox-api-data/transactions.sandbox.api.data.ts new file mode 100644 index 000000000..71da49beb --- /dev/null +++ b/apps/golden-sample-app-e2e/data/sandbox-api-data/transactions.sandbox.api.data.ts @@ -0,0 +1,16 @@ +import { + TransactionListDataType, + TransactionDetailDataType, +} from '@backbase-gsa/transactions-journey/e2e-tests'; +export const transactionListSandboxData: TransactionListDataType = { + size: 10, + searchExpectations: [{ term: 'pocket', count: 5 }], +}; + +export const transactionDetailSandboxData: TransactionDetailDataType = { + recipient: 'BP', + category: 'Gasoline/Fuel', + description: 'BP Global', + status: 'BILLED', + id: '8a82815f936800030193810b891452e0', +}; diff --git a/apps/golden-sample-app-e2e/fixtures/transactions.fixture.ts b/apps/golden-sample-app-e2e/fixtures/transactions.fixture.ts new file mode 100644 index 000000000..b5fae1428 --- /dev/null +++ b/apps/golden-sample-app-e2e/fixtures/transactions.fixture.ts @@ -0,0 +1,42 @@ +import { testWithAuth as baseTest } from '../page-objects/test-runner'; +import { mergeTests } from '@playwright/test'; +import { + TransactionFixture, + test as transferTest, +} from '@backbase-gsa/transactions-journey/e2e-tests'; +import { + transactionDetailMocksData, + transactionListMockData, +} from '../data/mocks-data'; +import { + transactionDetailSandboxData, + transactionListSandboxData, +} from '../data/sandbox-api-data'; +import { TestEnvironment } from 'test.model'; + +// Transactions test data per Env type +const testData: Partial< + Record> +> = { + // Mock data to run tests against mocks + [TestEnvironment.MOCKS]: { + detailsData: transactionDetailMocksData, + listData: transactionListMockData, + }, + // Sandbox data to run tests against sandbox env + [TestEnvironment.SANDBOX]: { + detailsData: transactionDetailSandboxData, + listData: transactionListSandboxData, + }, +}; + +export const test = mergeTests( + baseTest, + transferTest +).extend({ + // overrode default data based on environment config + detailsData: async ({ env }, use) => await use(testData[env]!.detailsData), + listData: async ({ env }, use) => await use(testData[env]!.listData), + // type of the user + userType: 'userWithNoContext', +}); diff --git a/apps/golden-sample-app-e2e/page-objects/test-runner.ts b/apps/golden-sample-app-e2e/page-objects/test-runner.ts index 755cf359b..07d30f3cd 100644 --- a/apps/golden-sample-app-e2e/page-objects/test-runner.ts +++ b/apps/golden-sample-app-e2e/page-objects/test-runner.ts @@ -2,13 +2,19 @@ import { test as baseTest } from '@playwright/test'; import { VisualValidator } from '../utils/visual-validator'; import { IdentityPage } from './pages/identity-page'; import { testConfig } from '../test-config'; +import { User } from '../data/data-types/user'; +import { readFile } from '../utils/read-file'; import 'dotenv/config'; +import { TestEnvironment, TestOptions } from '../../../test.model'; -export const test = baseTest.extend<{ - // Pages +interface TestRunnerOptions extends TestOptions { identityPage: IdentityPage; visual: VisualValidator; -}>({ + config: { users: Record }; + env: TestEnvironment; +} + +export const test = baseTest.extend({ identityPage: async ({ page }, use, testInfo) => { await use( new IdentityPage(page, testInfo, { url: testConfig.appBaseUrl() }) @@ -17,4 +23,31 @@ export const test = baseTest.extend<{ visual: async ({ page }, use) => { await use(new VisualValidator(page)); }, + configPath: ['', { option: true }], + config: async ({ configPath }, use) => { + await use(configPath ? readFile(configPath) : { users: {} }); + }, + testEnvironment: [TestEnvironment.MOCKS, { option: true }], + env: async ({ testEnvironment }, use) => { + await use(testEnvironment); + }, +}); + +export const testWithAuth = test.extend<{ + // Authentication + userType: string; + user: User; +}>({ + userType: ['', { option: true }], + user: async ({ config, userType }, use) => { + await use(config.users[userType]); + }, + storageState: async ({ page, identityPage, user, baseURL }, use) => { + if (user) { + await identityPage.login(user); + await page.goto(baseURL ?? ''); + await page.waitForTimeout(1000); + } + await use({ cookies: [], origins: [] }); + }, }); diff --git a/apps/golden-sample-app-e2e/project.json b/apps/golden-sample-app-e2e/project.json index acec423c2..2c8c7ed66 100644 --- a/apps/golden-sample-app-e2e/project.json +++ b/apps/golden-sample-app-e2e/project.json @@ -3,13 +3,54 @@ "$schema": "../../node_modules/nx/schemas/project-schema.json", "projectType": "application", "tags": [], - "implicitDependencies": ["golden-sample-app"], + "implicitDependencies": [ + "golden-sample-app" + ], "targets": { "e2e-chrome-mobile": { "executor": "nx:run-commands", "options": { "command": "npx playwright test --project 'mobile-chrome' --grep '@visual.+@responsive|@responsive.+@visual' --workers=2" } + }, + "e2e-localhost-ebp-sndbx": { + "executor": "@dot-build/serve-and-run-angular:run", + "options": { + "devServerTarget": "golden-sample-app:serve:ebp-sndbox", + "command": "npx playwright", + "args": [ + "test", + "--config=playwright.localhost.config.ts", + "--project=localhost-ebp-sndbx", + "--grep='@e2e'", + "--grep-invert='@error-handling'" + ] + } + }, + "e2e-localhost-mocks": { + "executor": "@dot-build/serve-and-run-angular:run", + "options": { + "devServerTarget": "golden-sample-app:serve:mocks", + "command": "npx playwright", + "args": [ + "test", + "--config=playwright.localhost.config.ts", + "--project=localhost-mocked", + "--grep='@e2e'", + "--grep-invert='@identity'" + ] + } + }, + "e2e-modelbank-stg": { + "executor": "@dot-build/serve-and-run-angular:run", + "options": { + "command": "npx playwright", + "args": [ + "test", + "--config=playwright.modelbank.config.ts", + "--project=remote-bus-stg" + ] + } } } -} +} \ No newline at end of file diff --git a/apps/golden-sample-app-e2e/specs/login.spec.ts b/apps/golden-sample-app-e2e/specs/login.spec.ts deleted file mode 100644 index ea1fea109..000000000 --- a/apps/golden-sample-app-e2e/specs/login.spec.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { expect } from '@playwright/test'; -import { test } from '../page-objects/test-runner'; -import { wrongUser } from '../data/credentials'; - -const i18n = { - identity: { - username: 'Username or email', - password: 'Password', - loginButton: 'Log in', - error: - 'Incorrect username or passwordPlease check your details and try again', - }, -}; - -test.describe.configure({ mode: 'parallel' }); - -test.describe('@feature @i18n @e2e Login tests', () => { - test('Empty user name', async ({ identityPage }) => { - identityPage.open(); - await test.step('Validate Input fields labels', async () => { - await expect - .soft(identityPage.userNameLabel, { - message: `Expect Username label: "${i18n.identity.username}"`, - }) - .toHaveText(i18n.identity.username); - await expect - .soft(identityPage.passwordLabel, { - message: `Expect Password label: "${i18n.identity.password}"`, - }) - .toHaveText(i18n.identity.password); - }); - await test.step(`Fill in credentials: "${wrongUser.username}/${wrongUser.password}"`, async () => { - await identityPage.userName.fill(wrongUser.username); - await identityPage.password.fill(wrongUser.password); - }); - await test.step('Try to login"', async () => { - await identityPage.loginButton.click(); - }); - await test.step('Validate Failed login error message', async () => { - await expect(identityPage.errorMessage, { - message: `Expect error message: "${i18n.identity.error}"`, - }).toHaveText(i18n.identity.error); - }); - }); -}); diff --git a/apps/golden-sample-app-e2e/specs/mb-login.spec.ts b/apps/golden-sample-app-e2e/specs/mb-login.spec.ts new file mode 100644 index 000000000..cc1ff8ea4 --- /dev/null +++ b/apps/golden-sample-app-e2e/specs/mb-login.spec.ts @@ -0,0 +1,49 @@ +import { expect } from '@playwright/test'; +import { test } from '../page-objects/test-runner'; +import { wrongUser } from '../data/credentials'; + +const i18n = { + identity: { + username: 'Username or email', + password: 'Password', + loginButton: 'Log in', + error: + 'Incorrect username or passwordPlease check your details and try again', + }, +}; + +test.describe.configure({ mode: 'parallel' }); + +test.describe( + 'Login tests', + { tag: ['@feature', '@i18n', '@e2e', '@identity'] }, + () => { + test('Empty user name', async ({ identityPage }) => { + identityPage.open(); + await test.step('Validate Input fields labels', async () => { + await expect + .soft(identityPage.userNameLabel, { + message: `Expect Username label: "${i18n.identity.username}"`, + }) + .toHaveText(i18n.identity.username); + await expect + .soft(identityPage.passwordLabel, { + message: `Expect Password label: "${i18n.identity.password}"`, + }) + .toHaveText(i18n.identity.password); + }); + await test.step(`Fill in credentials: "${wrongUser.username}/${wrongUser.password}"`, async () => { + await identityPage.userName.fill(wrongUser.username); + await identityPage.password.fill(wrongUser.password); + }); + await test.step('Try to login"', async () => { + await identityPage.loginButton.click(); + }); + await test.step('Validate Failed login error message', async () => { + await expect(identityPage.errorMessage, { + message: `Expect error message: "${i18n.identity.error}"`, + }).toHaveText(i18n.identity.error); + }); + }); + } +); diff --git a/apps/golden-sample-app-e2e/specs/transaction-details.spec.ts b/apps/golden-sample-app-e2e/specs/transaction-details.spec.ts new file mode 100644 index 000000000..a561fc023 --- /dev/null +++ b/apps/golden-sample-app-e2e/specs/transaction-details.spec.ts @@ -0,0 +1,4 @@ +import { test } from '../fixtures/transactions.fixture'; +import { testTransactionDetails } from '@backbase-gsa/transactions-journey/e2e-tests'; + +testTransactionDetails(test); diff --git a/apps/golden-sample-app-e2e/specs/transactions-list.spec.ts b/apps/golden-sample-app-e2e/specs/transactions-list.spec.ts new file mode 100644 index 000000000..13b47cef4 --- /dev/null +++ b/apps/golden-sample-app-e2e/specs/transactions-list.spec.ts @@ -0,0 +1,4 @@ +import { test } from '../fixtures/transactions.fixture'; +import { testTransactionsList } from '@backbase-gsa/transactions-journey/e2e-tests'; + +testTransactionsList(test); diff --git a/apps/golden-sample-app-e2e/specs/transactions.spec.ts b/apps/golden-sample-app-e2e/specs/transactions.spec.ts index 093da1fa7..e4bb87160 100644 --- a/apps/golden-sample-app-e2e/specs/transactions.spec.ts +++ b/apps/golden-sample-app-e2e/specs/transactions.spec.ts @@ -1,7 +1,7 @@ import { test } from '../page-objects/test-runner'; -test.beforeEach(async ({ page }) => { - await page.goto('transactions'); +test.beforeEach(async ({ page, baseURL }) => { + await page.goto(`${baseURL}/transactions`); }); test.describe.configure({ mode: 'parallel' }); diff --git a/apps/golden-sample-app-e2e/utils/read-file.ts b/apps/golden-sample-app-e2e/utils/read-file.ts new file mode 100644 index 000000000..3d17375ea --- /dev/null +++ b/apps/golden-sample-app-e2e/utils/read-file.ts @@ -0,0 +1,11 @@ +import fs from 'fs'; + +export const readFile = (path: string | undefined): T => { + if (!path) { + throw new Error( + 'Failed to read config file, since path to the file provided was "undefined"' + ); + } + const configContents = fs.readFileSync(path, 'utf-8'); + return JSON.parse(configContents) as T; +}; diff --git a/apps/golden-sample-app/project.json b/apps/golden-sample-app/project.json index 42df3c075..781f588e9 100644 --- a/apps/golden-sample-app/project.json +++ b/apps/golden-sample-app/project.json @@ -114,7 +114,8 @@ "executor": "@angular-devkit/build-angular:dev-server", "configurations": { "development": { - "buildTarget": "golden-sample-app:build:development" + "buildTarget": "golden-sample-app:build:development", + "proxyConfig": "./apps/golden-sample-app/proxy.stg-ebp.conf.js" }, "production": { "buildTarget": "golden-sample-app:build:production" @@ -123,8 +124,12 @@ "buildTarget": "golden-sample-app:build:nl" }, "mocks": { - "proxyConfig": "./apps/golden-sample-app/proxy.mocks.conf.json", + "proxyConfig": "./apps/golden-sample-app/proxy.mocks.conf.js", "buildTarget": "golden-sample-app:build:mocks" + }, + "ebp-sndbox": { + "buildTarget": "golden-sample-app:build:development", + "proxyConfig": "./apps/golden-sample-app/proxy.stg-ebp.conf.js" } }, "defaultConfiguration": "development" diff --git a/apps/golden-sample-app/proxy.mocks.conf.js b/apps/golden-sample-app/proxy.mocks.conf.js new file mode 100644 index 000000000..f9824d330 --- /dev/null +++ b/apps/golden-sample-app/proxy.mocks.conf.js @@ -0,0 +1,5 @@ +module.exports = [{ + context: '/api', + target: 'http://localhost:9999', + secure: false +}] diff --git a/apps/golden-sample-app/proxy.mocks.conf.json b/apps/golden-sample-app/proxy.mocks.conf.json deleted file mode 100644 index 03e9d42e6..000000000 --- a/apps/golden-sample-app/proxy.mocks.conf.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "context": "/api", - "target": "http://localhost:9999", - "secure": false -} diff --git a/apps/golden-sample-app/proxy.stg-ebp.conf.js b/apps/golden-sample-app/proxy.stg-ebp.conf.js new file mode 100644 index 000000000..c9af2542b --- /dev/null +++ b/apps/golden-sample-app/proxy.stg-ebp.conf.js @@ -0,0 +1,23 @@ +module.exports = [ + { + context: '/api', + target: 'https://app.stg.sdbxaz.azure.backbaseservices.com/', + secure: false, + changeOrigin: true, + bypass: function (req) { + req.headers['x-sdbxaz-api-key'] = + 'apisandbox-d0d8278f-5fc5-46fb-5fc5-d0b6e1cc8059'; + }, + }, + { + context: '/auth', + target: 'https://identity.stg.sdbxaz.azure.backbaseservices.com', + secure: false, + changeOrigin: true, + bypass: function (req) { + req.headers['x-sdbxaz-api-key'] = + 'apisandbox-d0d8278f-5fc5-46fb-5fc5-d0b6e1cc8059'; + }, + }, +]; +//https://identity.stg.sdbxaz.azure.backbaseservices.com \ No newline at end of file diff --git a/apps/golden-sample-app/src/app/app-routing.module.ts b/apps/golden-sample-app/src/app/app-routing.module.ts index 2fb2a258d..ef9b1cb3a 100644 --- a/apps/golden-sample-app/src/app/app-routing.module.ts +++ b/apps/golden-sample-app/src/app/app-routing.module.ts @@ -52,7 +52,7 @@ const routes: Routes = [ (m) => m.TransactionsJourneyBundleModule ), data: { - entitlements: triplets.canViewTransactions, + // entitlements: triplets.canViewTransactions, }, canActivate: [AuthGuard, UserContextGuard, EntitlementsGuard], }, diff --git a/apps/golden-sample-app/src/environments/environment.ts b/apps/golden-sample-app/src/environments/environment.ts index 5f69a8e55..58b5067cf 100644 --- a/apps/golden-sample-app/src/environments/environment.ts +++ b/apps/golden-sample-app/src/environments/environment.ts @@ -18,7 +18,7 @@ const mockProviders: Provider[] = [ export const environment: Environment = { production: false, - apiRoot: 'https://app.stg.sdbxaz.azure.backbaseservices.com/api', + apiRoot: '/api', mockProviders, locales: ['en-US', 'nl-NL'], common: { diff --git a/libs/transactions-journey/e2e-tests/fixture/index.ts b/libs/transactions-journey/e2e-tests/fixture/index.ts new file mode 100644 index 000000000..e8f45da2d --- /dev/null +++ b/libs/transactions-journey/e2e-tests/fixture/index.ts @@ -0,0 +1 @@ +export * from './transaction'; diff --git a/libs/transactions-journey/e2e-tests/fixture/transaction.ts b/libs/transactions-journey/e2e-tests/fixture/transaction.ts new file mode 100644 index 000000000..b8ecee834 --- /dev/null +++ b/libs/transactions-journey/e2e-tests/fixture/transaction.ts @@ -0,0 +1,21 @@ +import { test as baseTest } from '@playwright/test'; +import { + TransactionFixture, + TransactionDetailDataType, + TransactionListDataType, +} from '../model'; +import { TransactionDetailsPage, TransactionsListPage } from '../page-object'; + +export const test = baseTest.extend({ + detailsPage: async ({ page, baseURL }, use) => { + const pageObject = new TransactionDetailsPage(page, { baseURL }); + await use(pageObject); + }, + detailsData: {} as TransactionDetailDataType, // pass default data + listPage: async ({ page, baseURL }, use) => { + const pageObject = new TransactionsListPage(page, { baseURL }); + await use(pageObject); + }, + + listData: {} as TransactionListDataType, // pass default data +}); diff --git a/libs/transactions-journey/e2e-tests/index.ts b/libs/transactions-journey/e2e-tests/index.ts new file mode 100644 index 000000000..fda398c8b --- /dev/null +++ b/libs/transactions-journey/e2e-tests/index.ts @@ -0,0 +1,9 @@ +// Export everything from the tests lib from journey. +export * from './page-object/transaction-details.page'; +export * from './page-object/transactions-list.page'; + +export * from './specs/transaction-details.spec'; +export * from './specs/transactions-list.spec'; + +export * from './model'; +export * from './fixture'; diff --git a/libs/transactions-journey/e2e-tests/locators/transactions.locators.ts b/libs/transactions-journey/e2e-tests/locators/transactions.locators.ts new file mode 100644 index 000000000..07b161637 --- /dev/null +++ b/libs/transactions-journey/e2e-tests/locators/transactions.locators.ts @@ -0,0 +1,7 @@ +export const TRANSACTIONS_LOCATORS = { + item: 'bb-transaction-details dl > div', + itemTitle: 'dt', + itemValue: 'dd', + searchInput: 'bb-transactions-view bb-input-text-ui input', + transaction: 'bb-transactions-view bb-transaction-item', +}; diff --git a/libs/transactions-journey/e2e-tests/model/index.ts b/libs/transactions-journey/e2e-tests/model/index.ts new file mode 100644 index 000000000..e8f45da2d --- /dev/null +++ b/libs/transactions-journey/e2e-tests/model/index.ts @@ -0,0 +1 @@ +export * from './transaction'; diff --git a/libs/transactions-journey/e2e-tests/model/transaction.ts b/libs/transactions-journey/e2e-tests/model/transaction.ts new file mode 100644 index 000000000..dc1c1c49c --- /dev/null +++ b/libs/transactions-journey/e2e-tests/model/transaction.ts @@ -0,0 +1,26 @@ +import { TransactionDetailsPage } from '../page-object/transaction-details.page'; +import { TransactionsListPage } from '../page-object/transactions-list.page'; + +export interface TransactionDataType { + transactionList: TransactionListDataType; + transactionDetails: TransactionDetailDataType; +} + +export interface TransactionListDataType { + size: number; + searchExpectations: Array<{ term: string; count: number }>; +} +export interface TransactionDetailDataType { + recipient: string; + category: string; + description: string; + status: string; + id: string; +} + +export interface TransactionFixture { + detailsPage: TransactionDetailsPage; + detailsData: TransactionDetailDataType; + listPage: TransactionsListPage; + listData: TransactionListDataType; +} diff --git a/libs/transactions-journey/e2e-tests/page-object/index.ts b/libs/transactions-journey/e2e-tests/page-object/index.ts new file mode 100644 index 000000000..9b87fd2e5 --- /dev/null +++ b/libs/transactions-journey/e2e-tests/page-object/index.ts @@ -0,0 +1,2 @@ +export * from './transaction-details.page'; +export * from './transactions-list.page'; diff --git a/libs/transactions-journey/e2e-tests/page-object/transaction-details.page.ts b/libs/transactions-journey/e2e-tests/page-object/transaction-details.page.ts new file mode 100644 index 000000000..128a73eb7 --- /dev/null +++ b/libs/transactions-journey/e2e-tests/page-object/transaction-details.page.ts @@ -0,0 +1,33 @@ +import { Page } from '@playwright/test'; +import { TRANSACTIONS_LOCATORS } from '../locators/transactions.locators'; + +export class TransactionDetailsPage { + constructor( + private readonly page: Page, + private readonly config: { baseURL?: string } = {} + ) {} + private readonly locators = TRANSACTIONS_LOCATORS; + + async navigate(id: string) { + await this.page.goto(`${this.config.baseURL}/transactions/${id}`); + } + + async getDetails() { + await this.waitForVisibleDetails(); + + const details: Record = {}; + const items = await this.page.locator(this.locators.item).all(); + + for (const item of items) { + const title = await item.locator(this.locators.itemTitle).innerText(); + const value = await item.locator(this.locators.itemValue).innerText(); + details[title] = value; + } + + return details; + } + + async waitForVisibleDetails() { + await this.page.waitForSelector(this.locators.item); + } +} diff --git a/libs/transactions-journey/e2e-tests/page-object/transactions-list.page.ts b/libs/transactions-journey/e2e-tests/page-object/transactions-list.page.ts new file mode 100644 index 000000000..68ad3349b --- /dev/null +++ b/libs/transactions-journey/e2e-tests/page-object/transactions-list.page.ts @@ -0,0 +1,40 @@ +import { Page, Route } from '@playwright/test'; +import { TRANSACTIONS_LOCATORS } from '../locators/transactions.locators'; + +export class TransactionsListPage { + constructor( + private readonly page: Page, + private readonly config: { baseURL?: string } = {} + ) {} + private readonly locators = TRANSACTIONS_LOCATORS; + + async navigate() { + await this.page.goto(`${this.config.baseURL}/transactions`); + } + + async searchTransactions(searchTerm: string) { + const searchInput = this.page.locator(this.locators.searchInput); + await searchInput.fill(searchTerm); + } + + async getTransactionsNumber() { + await this.waitForVisibleTransactions(); + + return this.page.locator(this.locators.transaction).count(); + } + + async getTransactionListError() { + await this.page.route( + '**/transaction-manager/client-api/v2/transactions', + async (route: Route) => { + await route.fulfill({ + status: 500, + }); + } + ); + } + + async waitForVisibleTransactions() { + await this.page.waitForSelector(this.locators.transaction); + } +} diff --git a/libs/transactions-journey/e2e-tests/specs/transaction-details.spec.ts b/libs/transactions-journey/e2e-tests/specs/transaction-details.spec.ts new file mode 100644 index 000000000..2d78f7fff --- /dev/null +++ b/libs/transactions-journey/e2e-tests/specs/transaction-details.spec.ts @@ -0,0 +1,26 @@ +import { expect, TestType } from '@playwright/test'; +import { TransactionFixture } from '../model/transaction'; + +export function testTransactionDetails( + test: TestType +) { + test.describe( + 'Transactions details', + { tag: ['@e2e', '@transactions', '@transactions-details'] }, + () => { + test.beforeEach(async ({ detailsPage, detailsData }) => { + await detailsPage.navigate(detailsData.id); + }); + + test('should display correct transaction details', async ({ + detailsPage, + detailsData, + }) => { + const details = await detailsPage.getDetails(); + expect(details['Category:']).toEqual(detailsData.category); + expect(details['Description:']).toEqual(detailsData.description); + expect(details['Status:']).toEqual(detailsData.status); + }); + } + ); +} diff --git a/libs/transactions-journey/e2e-tests/specs/transactions-list.spec.ts b/libs/transactions-journey/e2e-tests/specs/transactions-list.spec.ts new file mode 100644 index 000000000..776cbbcac --- /dev/null +++ b/libs/transactions-journey/e2e-tests/specs/transactions-list.spec.ts @@ -0,0 +1,42 @@ +import { expect, TestType } from '@playwright/test'; +import { TransactionFixture } from '../model/transaction'; + +export function testTransactionsList( + test: TestType +) { + test.describe( + 'Transactions list', + { tag: ['@e2e', '@transactions', '@transactions-details'] }, + () => { + test.beforeEach(async ({ listPage }) => { + await listPage.navigate(); + }); + + test('should display transactions', async ({ listPage, listData }) => { + const transactionsNumber = await listPage.getTransactionsNumber(); + expect(transactionsNumber).toEqual(listData.size); + }); + + test('should filter transactions', async ({ listPage, listData }) => { + for (const expectation of listData.searchExpectations) { + await test.step(`Search transactions by "${expectation.term}" term`, async () => { + await listPage.searchTransactions(expectation.term); + }); + await test.step(`Validate number of transactions to be ${expectation.count}`, async () => { + const transactionsNumber = await listPage.getTransactionsNumber(); + expect(transactionsNumber).toEqual(expectation.count); + }); + } + }); + + test( + 'should display correct error state', + { tag: ['@error-handling'] }, + async ({ listPage }) => { + // Error state test placeholder + expect(true).toBe(true); + } + ); + } + ); +} diff --git a/package.json b/package.json index 9c1d1461c..9aca4ff66 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "analyze": "webpack-bundle-analyzer dist/golden-sample-app/stats.json", "ng": "nx", "start": "nx serve", - "start:mocks": "nx serve -c=mocks", + "start:mocks": "npm run mock-server & nx serve -c=mocks", "start:nl": "nx serve golden-sample-app:serve:nl", "start:docker": "docker-compose up --build", "test": "nx run-many --target=test --all", @@ -19,8 +19,10 @@ "affected:lint": "nx affected:lint", "affected:test": "nx affected:test", "xi18n": "ng extract-i18n golden-sample-app --output-path=apps/golden-sample-app/src/locale && xliffmerge --profile apps/golden-sample-app/src/xliffmerge.json nl-NL", - "e2e": "nx e2e", - "e2e-test": "npx playwright test --project 'Web Chrome' --grep '@e2e'", + "e2e": "npm run e2e-test-mocks && npm run e2e-test-sndbx-ci", + "e2e-test-mocks": "nx run golden-sample-app-e2e:e2e-localhost-mocks", + "e2e-test-sndbx-all":"nx run golden-sample-app-e2e:e2e-localhost-ebp-sndbx", + "e2e-test-sndbx-ci":"nx run golden-sample-app-e2e:e2e-localhost-ebp-sndbx --args='test --config=playwright.localhost.config.ts --project=localhost-ebp-sndbx --grep=@identity'", "e2e-test-responsive": "npx playwright test --project 'mobile-chrome' --grep '@visual.+@responsive|@responsive.+@visual' --workers=2", "lint": "nx run-many --all --target=lint", "format": "prettier --write \"{apps,libs}/**/*.{ts,md,html}\" \"!{apps,libs}/**/coverage\" ", diff --git a/playwright.config.ts b/playwright.config.ts index ef1c5a731..fc98f1258 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -7,7 +7,7 @@ const config: PlaywrightTestConfig = { testDir: './apps/golden-sample-app-e2e/specs/', retries: process.env['CI'] ? 1 : 0, grep: process.env['TEST_TAG'] ? RegExp(process.env['TEST_TAG']) : undefined, - outputDir: process.env['OUTPUT_DIR'] || './test-output', + outputDir: process.env['OUTPUT_DIR'] ?? './test-output', forbidOnly: !!process.env['CI'], workers: process.env['CI'] ? 4 : 1, expect: { @@ -20,7 +20,7 @@ const config: PlaywrightTestConfig = { globalSetup: require.resolve(__dirname + '/global-setup.ts'), use: { trace: 'retain-on-failure', - baseURL: process.env['BASE_URL'] || 'http://localhost:4200/', + baseURL: process.env['BASE_URL'] ?? 'http://localhost:4200/', ignoreHTTPSErrors: true, screenshot: 'only-on-failure', video: 'on-first-retry', @@ -76,4 +76,4 @@ const config: PlaywrightTestConfig = { ], }; -export default config; +export default config; \ No newline at end of file diff --git a/playwright.localhost.config.ts b/playwright.localhost.config.ts new file mode 100644 index 000000000..361a05d20 --- /dev/null +++ b/playwright.localhost.config.ts @@ -0,0 +1,71 @@ +import { defineConfig, devices } from '@playwright/test'; +import 'dotenv/config'; +import baseConfig from './playwright.config'; +import { join } from 'path'; +import { TestOptions, TestEnvironment } from './test.model'; + + +export default defineConfig({ + ...baseConfig, + projects: [ + /** + * Configuration for running tests with mock env + */ + { + name: 'localhost-mocked', + use: { + ...devices['Desktop Chrome'], + headless: true, + launchOptions: { + chromiumSandbox: false, + args: ['--disable-infobars', '--no-sandbox', '--incognito'], + }, + testEnvironment: TestEnvironment.MOCKS, + baseURL: 'http://localhost:4200/', + }, + }, + /** + * Configuration for running tests with sandbox env running locally and proxying to sandbox + */ + { + name: 'localhost-ebp-sndbx', + use: { + ...devices['Desktop Chrome'], + headless: true, + launchOptions: { + chromiumSandbox: false, + args: ['--disable-infobars', '--no-sandbox', '--incognito'], + }, + configPath: join(__dirname, 'apps/golden-sample-app-e2e/config/ebp-sndbx.config.json'), + testEnvironment: TestEnvironment.SANDBOX, + baseURL: 'http://localhost:4200/', + }, + testIgnore: /mocked-.*/, // We don't want to run tests which are created for mocks only + }, + /** + * Configuration for running tests with sandbox env running locally and proxying to sandbox + */ + { + name: 'localhost-mb-stg', + use: { + ...devices['Desktop Chrome'], + headless: true, + launchOptions: { + chromiumSandbox: false, + args: ['--disable-infobars', '--no-sandbox', '--incognito'], + }, + configPath: join(__dirname, 'apps/golden-sample-app-e2e/config/mb-stg.config.json'), + baseURL: 'http://localhost:4200/', + }, + testIgnore: /mocked-.*/, // We don't want to run tests which are created for mocks only + }, + ], + webServer: [ + { + command: 'npm run mock-server', + url: 'http://localhost:9999/dev-interface', + timeout: 30 * 1000, + reuseExistingServer: true, + }, + ], +}) diff --git a/playwright.modelbank.config.ts b/playwright.modelbank.config.ts new file mode 100644 index 000000000..9b5434cb1 --- /dev/null +++ b/playwright.modelbank.config.ts @@ -0,0 +1,64 @@ +import { defineConfig, devices } from '@playwright/test'; +import 'dotenv/config'; +import baseConfig from './playwright.config'; +import { join } from 'path'; + + + +export default defineConfig({ + ...baseConfig, + projects: [ + /** + * Configuration for running tests on ephemeral environment. + */ + { + name: 'remote-ephemeral', + use: { + ...devices['Desktop Chrome'], + viewport: { width: 1723, height: 896 }, + configPath: join(__dirname, 'apps/golden-sample-app-e2e/config/ephemeral.config.json'), // config for login details + baseURL: process.env['REMOTE_URL'] ?? 'http://localhost:4200', // path to the ephemeral env + }, + testIgnore: /mocked-.*/, // we want to run every test here except mocked ones + }, + /** + * Configuration for modelbank staging env. + */ + { + name: 'remote-mb-stg', + use: { + ...devices['Desktop Chrome'], + viewport: { width: 1723, height: 896 }, + configPath: join(__dirname, 'apps/golden-sample-app-e2e/config/mb-stg.config.json'), + baseURL: process.env['REMOTE_URL'] ?? 'https://business.mb-stg.reference.azure.backbaseservices.com', + }, + testMatch: /mb-.*\.(e2e-spec|spec)\.ts/, // we just want to run all tests with file names starting from mb-*.ts. + }, + /** + * Configuration for modelbank stable env. + */ + { + name: 'remote-mb-stable', + use: { + ...devices['Desktop Chrome'], + viewport: { width: 1723, height: 896 }, + configPath: join(__dirname, 'src/config/mb-stable.config.json'), + baseURL: process.env['REMOTE_URL'] ?? 'https://business.mb-stable.reference.azure.backbaseservices.com', + }, + testMatch: /mb-.*\.(e2e-spec|spec)\.ts/, // we just want to run all tests with file names starting from mb-*.ts. + }, + /** + * Configuration for US latest env. + */ + { + name: 'remote-us-latest', + use: { + ...devices['Desktop Chrome'], + viewport: { width: 1723, height: 896 }, + configPath: join(__dirname, 'src/config/us-latest.config.json'), + baseURL: process.env['REMOTE_URL'] ?? 'https://business.us-latest.rndbb.azure.backbaseservices.com', + }, + testMatch: /mb-.*\.(e2e-spec|spec)\.ts/, // we just want to run all tests with file names starting from mb-*.ts. + }, + ], +}) diff --git a/test.model.ts b/test.model.ts new file mode 100644 index 000000000..12f82ec9c --- /dev/null +++ b/test.model.ts @@ -0,0 +1,11 @@ +export interface TestOptions{ + configPath: string; + testEnvironment: TestEnvironment; +} + +export enum TestEnvironment { + MOCKS= 'mocks', + SANDBOX = 'sandbox', + EPHEMERAL = 'ephemeral', + MODELBANK_STAGING = 'mb-stg' +} \ No newline at end of file diff --git a/tsconfig.base.json b/tsconfig.base.json index 5c3db46b8..6fa96e579 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -65,6 +65,9 @@ "@backbase-gsa/transactions-journey": [ "libs/transactions-journey/shell/src/index.ts" ], + "@backbase-gsa/transactions-journey/e2e-tests": [ + "libs/transactions-journey/e2e-tests/index.ts" + ], "@backbase-gsa/transfer-journey": [ "libs/transfer-journey/shell/src/index.ts" ], From e3a297b78c43eab84e8672f73845933de2ec978f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 12:38:58 +0100 Subject: [PATCH 2/2] build(deps-dev): bump eslint-plugin-playwright from 0.12.0 to 0.22.2 (#305) Bumps [eslint-plugin-playwright](https://github.com/playwright-community/eslint-plugin-playwright) from 0.12.0 to 0.22.2. - [Release notes](https://github.com/playwright-community/eslint-plugin-playwright/releases) - [Changelog](https://github.com/playwright-community/eslint-plugin-playwright/blob/main/CHANGELOG.md) - [Commits](https://github.com/playwright-community/eslint-plugin-playwright/compare/v0.12.0...v0.22.2) --- updated-dependencies: - dependency-name: eslint-plugin-playwright dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 40 +++++++++++++++++++++++++++++++++++----- package.json | 2 +- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5bea46994..84dbbb3b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -85,7 +85,7 @@ "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "^2.31.0", "eslint-plugin-jsdoc": "^50.6.1", - "eslint-plugin-playwright": "^0.12.0", + "eslint-plugin-playwright": "^0.22.2", "eslint-plugin-prefer-arrow": "^1.2.3", "find-cache-dir": "^3.3.2", "jest": "^29.4.1", @@ -14320,13 +14320,16 @@ } }, "node_modules/eslint-plugin-playwright": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-0.12.0.tgz", - "integrity": "sha512-KXuzQjVzca5irMT/7rvzJKsVDGbQr43oQPc8i+SLEBqmfrTxlwMwRqfv9vtZqh4hpU0jmrnA/EOfwtls+5QC1w==", + "version": "0.22.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-0.22.2.tgz", + "integrity": "sha512-LtOB9myIX1O7HHqg9vtvBLjvXq1MXKuXIcD1nS+qZiMUJV6s9HBdilURAr9pIFc9kEelbVF54hOJ8pMxHvJP7g==", "dev": true, + "dependencies": { + "globals": "^13.23.0" + }, "peerDependencies": { "eslint": ">=7", - "eslint-plugin-jest": ">=24" + "eslint-plugin-jest": ">=25" }, "peerDependenciesMeta": { "eslint-plugin-jest": { @@ -14334,6 +14337,33 @@ } } }, + "node_modules/eslint-plugin-playwright/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-playwright/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint-plugin-prefer-arrow": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/eslint-plugin-prefer-arrow/-/eslint-plugin-prefer-arrow-1.2.3.tgz", diff --git a/package.json b/package.json index 9aca4ff66..c162b5c97 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "^2.31.0", "eslint-plugin-jsdoc": "^50.6.1", - "eslint-plugin-playwright": "^0.12.0", + "eslint-plugin-playwright": "^0.22.2", "eslint-plugin-prefer-arrow": "^1.2.3", "find-cache-dir": "^3.3.2", "jest": "^29.4.1",