diff --git a/.gitignore b/.gitignore index 2ae631b349b..8a2eedc1dd1 100644 --- a/.gitignore +++ b/.gitignore @@ -411,3 +411,8 @@ stats.csv .vscode/settings.json .vscode/tasks.json .vscode/launch.json +/test-results/ +/blob-report/ +/playwright/.cache/ +/frontend/testing/playwright/playwright-report +.playwright/ diff --git a/frontend/jest.config.js b/frontend/jest.config.js index 36a44f54c7d..55c6e2f727a 100644 --- a/frontend/jest.config.js +++ b/frontend/jest.config.js @@ -50,7 +50,7 @@ const config = { '^@studio/components': path.join(__dirname, 'libs/studio-components/src/$1'), '^@studio/pure-functions': path.join(__dirname, 'libs/studio-pure-functions/src/$1'), }, - testRegex: '(\\.(test|spec))\\.(ts|tsx)$', + testRegex: '(\\.(test))\\.(ts|tsx)$', moduleFileExtensions: ['ts', 'tsx', 'js'], testEnvironment: 'jsdom', setupFilesAfterEnv: [path.join(__dirname, 'testing/setupTests.ts')], diff --git a/frontend/testing/.eslintignore b/frontend/testing/.eslintignore deleted file mode 100644 index 72e8ffc0db8..00000000000 --- a/frontend/testing/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -* diff --git a/frontend/testing/playwright/README.md b/frontend/testing/playwright/README.md new file mode 100644 index 00000000000..360d09f4fd4 --- /dev/null +++ b/frontend/testing/playwright/README.md @@ -0,0 +1,45 @@ +# Get started with Playwright + +Team Studio employs Playwright for end-to-end (e2e) testing. This README.md is designed to guide you through the initial setup. +It's crucial to bear in mind that each team member is responsible for keeping this README.md up-to-date. Your contributions are +highly encouraged to enhance the document and facilitate an easier onboarding process for your fellow team members. + +## Test Strategy + +Team Studio has decided to test various user journeys. A user journey may, for example, involve adding a field to the data model. +In such cases, the testing process should not only encompass the data model page itself, but we must also ensure that the data model +and the field are accessible on all pages integrated with the data model. This ensures a comprehensive verification that our solution +operates seamlessly across different pages. In this way, we can better ensure that things are integrated as they should be cross pages. + +More documentation and examples will be added after we have written our first scenario. + + +## Setup +To initiate test execution and writing tests, start by running the setup.js script located at the file path `/development/setup.js`. +For more information, refer to the `README.md` located at the root of the monorepo. The reason this is needed is to ensure +you have setup you local environment. If you already have a local environment up and running, you can skip this part. + +After executing the mentioned `setup.js` script, you are ready to set up Playwright. Simply run `yarn setup:playwright` to generate a `.env` +file for localhost. Then, execute the tests using the following command: `yarn test:all`. + +## Change Environment +If you wish to run tests against an environment other than `studio.localhost`, you can do so by modifying your `.env` file. In the `.env` file, +locate a variable named `PLAYWRIGHT_TEST_BASE_URL`, which is set to `studio.localhost` by default. It is automatically configured for you when running `yarn setup:playwright`. + + +## .ENV file +`.env` that is generated by the setup script looks like following: +``` +PLAYWRIGHT_TEST_BASE_URL=http://studio.localhost +PLAYWRIGHT_USER=<> +PLAYWRIGHT_PASS=<> +PLAYWRIGHT_DESIGNER_APP_NAME=<> +GITEA_ACCESS_TOKEN=<> +``` + +## Short Step By Step Guide +This is a short step-by-step guide with minimum needed explanation to get started. + +1. Install the dependencies within this package by running y`yarn install`. +2. Install browsers and set up Playwright with local `.env` by executing `yarn setup:playwright`. +3. You are now ready to execute tests using the command `yarn test:all`. diff --git a/frontend/testing/playwright/extenders/testExtend.ts b/frontend/testing/playwright/extenders/testExtend.ts new file mode 100644 index 00000000000..edc00441df3 --- /dev/null +++ b/frontend/testing/playwright/extenders/testExtend.ts @@ -0,0 +1,13 @@ +import { test as base } from '@playwright/test'; + +export type ExtendedTestOptions = { + testAppName: string; +}; + +// Extends the default test to support custom parameters such as appName for our test app +export const test = base.extend({ + testAppName: [process.env.PLAYWRIGHT_DESIGNER_APP_NAME, { option: true }], +}); + +const describe = test.describe; +export { describe }; diff --git a/frontend/testing/playwright/helpers/BasePage.ts b/frontend/testing/playwright/helpers/BasePage.ts new file mode 100644 index 00000000000..226db62842d --- /dev/null +++ b/frontend/testing/playwright/helpers/BasePage.ts @@ -0,0 +1,26 @@ +import * as nbTexts from '@altinn-studio/language/src/nb.json'; +import * as enTexts from '@altinn-studio/language/src/en.json'; +import { Page } from '@playwright/test'; +import { RouterRoute } from './RouterRoute'; +import { Environment } from './StudioEnvironment'; + +type Locale = 'nb' | 'en'; +type TextKey = keyof typeof nbTexts | keyof typeof enTexts; + +const localeTextMap: Record = { + nb: nbTexts, + en: enTexts, +}; + +export class BasePage extends RouterRoute { + public readonly page: Page; + + constructor(page: Page, environment?: Environment) { + super(environment); + this.page = page; + } + + public textMock(key: TextKey, locale: Locale = 'nb'): string { + return localeTextMap[locale][key] || key; + } +} diff --git a/frontend/testing/playwright/helpers/Gitea.ts b/frontend/testing/playwright/helpers/Gitea.ts new file mode 100644 index 00000000000..bfc3c15ecfc --- /dev/null +++ b/frontend/testing/playwright/helpers/Gitea.ts @@ -0,0 +1,15 @@ +import { StudioEnvironment, Environment } from './StudioEnvironment'; + +export class Gitea extends StudioEnvironment { + public giteaAccessToken: string = process.env.GITEA_ACCESS_TOKEN; + + constructor(environment?: Environment) { + super(environment); + } + + public getDeleteAppEndpoint(environment?: Environment): string { + const org = environment?.org || this.org; + const app = environment?.app || this.app; + return `/repos/api/v1/repos/${org}/${app}?token=${this.giteaAccessToken}`; + } +} diff --git a/frontend/testing/playwright/helpers/RouterRoute.ts b/frontend/testing/playwright/helpers/RouterRoute.ts new file mode 100644 index 00000000000..5d7ac4657c3 --- /dev/null +++ b/frontend/testing/playwright/helpers/RouterRoute.ts @@ -0,0 +1,36 @@ +import { Environment, StudioEnvironment } from './StudioEnvironment'; + +type SupportedRoutes = 'altinnLoginPage' | 'dashboard' | 'dashboardCreateApp' | 'editorOverview'; + +type RouterRoutes = Record; + +const routerRoutes: RouterRoutes = { + altinnLoginPage: '/', + dashboard: '/dashboard', + dashboardCreateApp: '/dashboard/self/new', + editorOverview: `/editor/{{org}}/{{app}}/overview`, +}; + +export class RouterRoute extends StudioEnvironment { + constructor(environment: Environment) { + super(environment); + } + + public getRoute(route: SupportedRoutes): string { + const routerRoute: string = routerRoutes[route]; + + if (this.includesOrgAndApp(routerRoute)) { + return this.replaceOrgAndMap(routerRoute); + } + + return routerRoute; + } + + private replaceOrgAndMap(route: string): string { + return route.replace('{{org}}', this.org).replace('{{app}}', this.app); + } + + private includesOrgAndApp(route: string): boolean { + return route.includes('{{org}}') || route.includes('{{app}}'); + } +} diff --git a/frontend/testing/playwright/helpers/StudioEnvironment.ts b/frontend/testing/playwright/helpers/StudioEnvironment.ts new file mode 100644 index 00000000000..611a7622430 --- /dev/null +++ b/frontend/testing/playwright/helpers/StudioEnvironment.ts @@ -0,0 +1,19 @@ +export type Environment = { + org?: string; + app?: string; +}; + +export class StudioEnvironment { + public readonly org: string; + public app: string; + public readonly designerAppName: string; + + constructor(private environment?: Environment) { + this.org = this.environment?.org ?? process.env.PLAYWRIGHT_USER; + this.app = this.environment?.app ?? process.env.PLAYWRIGHT_DESIGNER_APP_NAME; + } + + public updateAppNameEnv(appName: string): void { + this.app = appName; + } +} diff --git a/frontend/testing/playwright/package.json b/frontend/testing/playwright/package.json new file mode 100644 index 00000000000..0525d907b6d --- /dev/null +++ b/frontend/testing/playwright/package.json @@ -0,0 +1,21 @@ +{ + "name": "playwright-studio", + "private": true, + "version": "1.0.0", + "packageManager": "yarn@4.0.2", + "dependencies": { + "dotenv": "^16.3.1" + }, + "devDependencies": { + "@playwright/test": "^1.40.1", + "@types/dotenv": "^8.2.0", + "@types/node": "^20.10.5", + "ts-node": "^10.9.2" + }, + "scripts": { + "setup:playwright": "playwright install --with-deps && yarn setup:local:env", + "setup:playwright:ci": "playwright install --with-deps", + "setup:local:env": "ts-node ./scripts/setup.ts", + "test:all": "playwright test --config ./playwright.config.ts" + } +} diff --git a/frontend/testing/playwright/pages/CreateServicePage.ts b/frontend/testing/playwright/pages/CreateServicePage.ts new file mode 100644 index 00000000000..38f4d954b1c --- /dev/null +++ b/frontend/testing/playwright/pages/CreateServicePage.ts @@ -0,0 +1,29 @@ +import { Page } from '@playwright/test'; +import { BasePage } from '../helpers/BasePage'; + +export class CreateServicePage extends BasePage { + constructor(page: Page) { + super(page); + } + + public async loadCreateAppFormPage(): Promise { + await this.page.goto(this.getRoute('dashboardCreateApp')); + } + + public async verifyCreateAppFormPage(): Promise { + await this.page.waitForURL(this.getRoute('dashboardCreateApp')); + } + + public async writeAppName(appName: string): Promise { + await this.page.getByLabel(this.textMock('dashboard.name')).fill(appName); + return this.updateAppNameEnv(appName); + } + + public async clickOnCreateAppButton(): Promise { + await this.page.getByRole('button', { name: this.textMock('dashboard.create_service_btn') }).click(); + } + + public async verifyIsNavigatedToOverviewPage(): Promise { + await this.page.waitForURL(this.getRoute('editorOverview')); + } +} diff --git a/frontend/testing/playwright/pages/DashboardPage.ts b/frontend/testing/playwright/pages/DashboardPage.ts new file mode 100644 index 00000000000..1cfcd2539d1 --- /dev/null +++ b/frontend/testing/playwright/pages/DashboardPage.ts @@ -0,0 +1,20 @@ +import { BasePage } from '../helpers/BasePage'; +import { Page } from '@playwright/test'; + +export class DashboardPage extends BasePage { + constructor(page: Page) { + super(page); + } + + public async loadDashboardPage(): Promise { + await this.page.goto(this.getRoute('dashboard')); + } + + public async verifyDashboardPage(): Promise { + await this.page.waitForURL(this.getRoute('dashboard')); + } + + public async clickOnCreateAppLink(): Promise { + await this.page.getByRole('link', { name: this.textMock('dashboard.new_service') }).click(); + } +} diff --git a/frontend/testing/playwright/pages/LoginPage.ts b/frontend/testing/playwright/pages/LoginPage.ts new file mode 100644 index 00000000000..25d6546611c --- /dev/null +++ b/frontend/testing/playwright/pages/LoginPage.ts @@ -0,0 +1,45 @@ +import { Page } from '@playwright/test'; +import { BasePage } from '../helpers/BasePage'; + +// Since this page is a Razor page, it's not using the nb/en.json files, which are used in the frontend. +const loginPageTexts: Record = { + login: 'logg inn', + username: 'Brukernavn eller epost', + password: 'Passord', +}; + +export class LoginPage extends BasePage { + private readonly authStorageFile: string = '.playwright/auth/user.json'; + + constructor(page: Page) { + super(page); + } + + public async goToAltinnLoginPage(): Promise { + await this.page.goto(this.getRoute('altinnLoginPage')); + } + + public async goToGiteaLoginPage(): Promise { + await this.page.getByRole('button', { name: loginPageTexts['login'] }).click(); + } + + public async writeUsername(username: string): Promise { + return await this.page.getByLabel(loginPageTexts['username']).fill(username); + } + + public async writePassword(password: string): Promise { + return await this.page.getByLabel(loginPageTexts['password']).fill(password); + } + + public async clickLoginButton(): Promise { + return await this.page.getByRole('button', { name: loginPageTexts['login'] }).click(); + } + + public async confirmSuccessfulLogin(): Promise { + return this.page.waitForURL(this.getRoute('dashboard')); + } + + public async addSessionToSharableStorage() { + return await this.page.context().storageState({ path: this.authStorageFile }); + } +} diff --git a/frontend/testing/playwright/playwright.config.ts b/frontend/testing/playwright/playwright.config.ts new file mode 100644 index 00000000000..13f96722da1 --- /dev/null +++ b/frontend/testing/playwright/playwright.config.ts @@ -0,0 +1,46 @@ +import { defineConfig, devices } from '@playwright/test'; +import { config } from 'dotenv'; +import { ExtendedTestOptions } from './extenders/testExtend'; + +config(); + +export default defineConfig({ + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: 'html', + use: { + locale: 'nb-NO', + timezoneId: 'Europe/Oslo', + trace: 'on-first-retry', + }, + + projects: [ + { name: 'setup', testMatch: /.*\.setup\.ts/ }, + { + name: 'create-app-only', + dependencies: ['setup'], + testDir: './tests/create-app-only/', + testMatch: '*.spec.ts', + teardown: 'teardown-create-app-only', + use: { + ...devices['Desktop Chrome'], + baseURL: process.env.PLAYWRIGHT_TEST_BASE_URL, + storageState: '.playwright/auth/user.json', + testAppName: 'simple-app-test', + headless: true, + }, + }, + { + name: 'teardown-create-app-only', + testDir: './tests/create-app-only/', + testMatch: '*create-app-only.teardown.ts', + use: { + baseURL: process.env.PLAYWRIGHT_TEST_BASE_URL, + testAppName: 'simple-app-test', + }, + }, + ], +}); + diff --git a/frontend/testing/playwright/scripts/setup.ts b/frontend/testing/playwright/scripts/setup.ts new file mode 100644 index 00000000000..8b43c095870 --- /dev/null +++ b/frontend/testing/playwright/scripts/setup.ts @@ -0,0 +1,68 @@ +import path from 'path'; +import fs from 'fs'; +import os from 'os'; + +const giteaApi = require('../../../../development/utils/gitea-api'); + +// Configure the dotenv to read form the .env file on root of monorepo. +require('dotenv').config({ path: path.resolve(__dirname, '../../../../.env') }); + +const environment: Record = { + PLAYWRIGHT_TEST_BASE_URL: 'http://studio.localhost', + PLAYWRIGHT_DESIGNER_APP_NAME: 'auto-test-app', + + PLAYWRIGHT_USER: process.env.GITEA_CYPRESS_USER, + PLAYWRIGHT_PASS: process.env.GITEA_CYPRESS_PASS, + GITEA_ACCESS_TOKEN: null, +}; + +const createGiteaAccessToken = async (): Promise => { + const result = await giteaApi({ + path: `/repos/api/v1/users/${process.env.GITEA_ADMIN_USER}/tokens`, + method: 'POST', + user: process.env.GITEA_ADMIN_USER, + pass: process.env.GITEA_ADMIN_PASS, + body: { + name: 'setup.ts' + ' ' + Date.now(), + scopes: [ + 'write:activitypub', + 'write:admin', + 'write:issue', + 'write:misc', + 'write:notification', + 'write:organization', + 'write:package', + 'write:repository', + 'write:user', + ], + }, + }); + environment.GITEA_ACCESS_TOKEN = result.sha1; +}; + +const getEnvFilePath = (): string => { + return path.resolve(__dirname, '..', '.env'); +}; + +const mapEnvironment = () => { + return Object.keys(environment) + .map((key) => [key, environment[key]].join('=')) + .join(os.EOL); +}; + +const updateEnvironmentVars = async (): Promise => { + await createGiteaAccessToken(); + const filePath: string = getEnvFilePath(); + console.table(environment); + fs.writeFileSync(filePath, mapEnvironment(), { encoding: 'utf8', flag: 'w' }); +}; + +(async (): Promise => { + console.log('----- SETUP PLAYWRIGHT ENVIRONMENT VARIABLES STARTED -----'); + if (!environment.PLAYWRIGHT_USER || !environment.PLAYWRIGHT_PASS) { + console.error('Ensure to run `node setup.js` within development folder on root.'); + return; + } + await updateEnvironmentVars(); + console.log('----- SETUP PLAYWRIGHT ENVIRONMENT VARIABLES DONE -----'); +})(); diff --git a/frontend/testing/playwright/tests/auth.setup.ts b/frontend/testing/playwright/tests/auth.setup.ts new file mode 100644 index 00000000000..7dc92cddddb --- /dev/null +++ b/frontend/testing/playwright/tests/auth.setup.ts @@ -0,0 +1,14 @@ +import { test as setup } from '@playwright/test'; +import { LoginPage } from '../pages/LoginPage'; + +setup('authenticate user', async ({ page }): Promise => { + const loginPage = new LoginPage(page); + + await loginPage.goToAltinnLoginPage(); + await loginPage.goToGiteaLoginPage(); + await loginPage.writeUsername(process.env.PLAYWRIGHT_USER); + await loginPage.writePassword(process.env.PLAYWRIGHT_PASS); + await loginPage.clickLoginButton(); + await loginPage.confirmSuccessfulLogin(); + await loginPage.addSessionToSharableStorage(); +}); diff --git a/frontend/testing/playwright/tests/create-app-only/create-app-only.spec.ts b/frontend/testing/playwright/tests/create-app-only/create-app-only.spec.ts new file mode 100644 index 00000000000..6c28accf5be --- /dev/null +++ b/frontend/testing/playwright/tests/create-app-only/create-app-only.spec.ts @@ -0,0 +1,23 @@ +import { test } from '../../extenders/testExtend'; +import { CreateServicePage } from '../../pages/CreateServicePage'; +import { DashboardPage } from '../../pages/DashboardPage'; + +test('should load dashboard and the user should navigate to create app form', async ({ + page, +}): Promise => { + const dashboardPage = new DashboardPage(page); + const createServicePage = new CreateServicePage(page); + + await dashboardPage.loadDashboardPage(); + await dashboardPage.verifyDashboardPage(); + await dashboardPage.clickOnCreateAppLink(); + await createServicePage.verifyCreateAppFormPage(); +}); + +test('should be able to create new app', async ({ page, testAppName }): Promise => { + const createServicePage = new CreateServicePage(page); + await createServicePage.loadCreateAppFormPage(); + await createServicePage.writeAppName(testAppName); + await createServicePage.clickOnCreateAppButton(); + await createServicePage.verifyIsNavigatedToOverviewPage(); +}); diff --git a/frontend/testing/playwright/tests/create-app-only/create-app-only.teardown.ts b/frontend/testing/playwright/tests/create-app-only/create-app-only.teardown.ts new file mode 100644 index 00000000000..f4effb16bcf --- /dev/null +++ b/frontend/testing/playwright/tests/create-app-only/create-app-only.teardown.ts @@ -0,0 +1,9 @@ +import { expect } from '@playwright/test'; +import { test } from '../../extenders/testExtend'; +import { Gitea } from '../../helpers/Gitea'; + +test('should teardown simple-schema-app test', async ({ request, testAppName }) => { + const gitea = new Gitea(); + const response = await request.delete(gitea.getDeleteAppEndpoint({ app: testAppName })); + expect(response.ok()).toBeTruthy(); +}); diff --git a/frontend/testing/playwright/tsconfig.json b/frontend/testing/playwright/tsconfig.json new file mode 100644 index 00000000000..e2fc01de5e3 --- /dev/null +++ b/frontend/testing/playwright/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tsconfig.json" +} diff --git a/package.json b/package.json index 73b0d556ea0..a4b8ccf7b5c 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,8 @@ "syncpack": "npx syncpack fix-mismatches && npx syncpack format && npx syncpack set-semver-ranges", "test": "jest --maxWorkers=50% --config=frontend/jest.config.js", "test:ci": "jest --ci --coverage --max-workers=2 --cacheDirectory=$(yarn config get cacheFolder) --config=frontend/jest.config.js", - "typecheck": "yarn workspaces foreach -A run typecheck" + "typecheck": "yarn workspaces foreach -A run typecheck", + "playwright:test:all": "yarn workspace playwright-studio test:all" }, "syncpack": { "semverRange": "" @@ -116,6 +117,7 @@ "frontend/studio-root", "frontend/testing/mockend", "frontend/testing/cypress", + "frontend/testing/playwright", "frontend/packages/schema-editor", "frontend/packages/schema-model", "frontend/packages/shared", diff --git a/yarn.lock b/yarn.lock index b73be4ec462..9d281e970a9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1666,6 +1666,15 @@ __metadata: languageName: node linkType: hard +"@cspotcode/source-map-support@npm:^0.8.0": + version: 0.8.1 + resolution: "@cspotcode/source-map-support@npm:0.8.1" + dependencies: + "@jridgewell/trace-mapping": "npm:0.3.9" + checksum: b6e38a1712fab242c86a241c229cf562195aad985d0564bd352ac404be583029e89e93028ffd2c251d2c407ecac5fb0cbdca94a2d5c10f29ac806ede0508b3ff + languageName: node + linkType: hard + "@cypress/request@npm:^3.0.0": version: 3.0.1 resolution: "@cypress/request@npm:3.0.1" @@ -2673,7 +2682,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/resolve-uri@npm:^3.1.0": +"@jridgewell/resolve-uri@npm:^3.0.3, @jridgewell/resolve-uri@npm:^3.1.0": version: 3.1.1 resolution: "@jridgewell/resolve-uri@npm:3.1.1" checksum: 64d59df8ae1a4e74315eb1b61e012f1c7bc8aac47a3a1e683f6fe7008eab07bc512a742b7aa7c0405685d1421206de58c9c2e6adbfe23832f8bd69408ffc183e @@ -2721,6 +2730,16 @@ __metadata: languageName: node linkType: hard +"@jridgewell/trace-mapping@npm:0.3.9": + version: 0.3.9 + resolution: "@jridgewell/trace-mapping@npm:0.3.9" + dependencies: + "@jridgewell/resolve-uri": "npm:^3.0.3" + "@jridgewell/sourcemap-codec": "npm:^1.4.10" + checksum: 83deafb8e7a5ca98993c2c6eeaa93c270f6f647a4c0dc00deb38c9cf9b2d3b7bf15e8839540155247ef034a052c0ec4466f980bf0c9e2ab63b97d16c0cedd3ff + languageName: node + linkType: hard + "@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.9": version: 0.3.17 resolution: "@jridgewell/trace-mapping@npm:0.3.17" @@ -3276,6 +3295,17 @@ __metadata: languageName: node linkType: hard +"@playwright/test@npm:^1.40.1": + version: 1.40.1 + resolution: "@playwright/test@npm:1.40.1" + dependencies: + playwright: "npm:1.40.1" + bin: + playwright: cli.js + checksum: 77bfc3d4a101fd996309de3a67256117f671952764404727e520a4d512ba2e528d8f9e0b28ef9b2851664a651a5d002a0b05993e313077a2fa640bb430f38b25 + languageName: node + linkType: hard + "@popperjs/core@npm:^2.11.8": version: 2.11.8 resolution: "@popperjs/core@npm:2.11.8" @@ -4216,6 +4246,34 @@ __metadata: languageName: node linkType: hard +"@tsconfig/node10@npm:^1.0.7": + version: 1.0.9 + resolution: "@tsconfig/node10@npm:1.0.9" + checksum: a33ae4dc2a621c0678ac8ac4bceb8e512ae75dac65417a2ad9b022d9b5411e863c4c198b6ba9ef659e14b9fb609bbec680841a2e84c1172df7a5ffcf076539df + languageName: node + linkType: hard + +"@tsconfig/node12@npm:^1.0.7": + version: 1.0.11 + resolution: "@tsconfig/node12@npm:1.0.11" + checksum: 5ce29a41b13e7897a58b8e2df11269c5395999e588b9a467386f99d1d26f6c77d1af2719e407621412520ea30517d718d5192a32403b8dfcc163bf33e40a338a + languageName: node + linkType: hard + +"@tsconfig/node14@npm:^1.0.0": + version: 1.0.3 + resolution: "@tsconfig/node14@npm:1.0.3" + checksum: 19275fe80c4c8d0ad0abed6a96dbf00642e88b220b090418609c4376e1cef81bf16237bf170ad1b341452feddb8115d8dd2e5acdfdea1b27422071163dc9ba9d + languageName: node + linkType: hard + +"@tsconfig/node16@npm:^1.0.2": + version: 1.0.4 + resolution: "@tsconfig/node16@npm:1.0.4" + checksum: 202319785901f942a6e1e476b872d421baec20cf09f4b266a1854060efbf78cde16a4d256e8bc949d31e6cd9a90f1e8ef8fb06af96a65e98338a2b6b0de0a0ff + languageName: node + linkType: hard + "@types/aria-query@npm:^5.0.1": version: 5.0.1 resolution: "@types/aria-query@npm:5.0.1" @@ -4325,6 +4383,15 @@ __metadata: languageName: node linkType: hard +"@types/dotenv@npm:^8.2.0": + version: 8.2.0 + resolution: "@types/dotenv@npm:8.2.0" + dependencies: + dotenv: "npm:*" + checksum: a1f524da7dc18b09bdb6c2613b9abbbe8ca575621cce4625efc7f98daf2ba07c7dbab622c9bb394507a12c5c515f9cbbbba4aeec17b801c88faeb8e8e656d460 + languageName: node + linkType: hard + "@types/eslint-scope@npm:^3.7.3": version: 3.7.4 resolution: "@types/eslint-scope@npm:3.7.4" @@ -4559,6 +4626,15 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:^20.10.5": + version: 20.10.7 + resolution: "@types/node@npm:20.10.7" + dependencies: + undici-types: "npm:~5.26.4" + checksum: 4a1ba3fb7fc0a262c6b2d5ec763cec7abf5b5d6b2d277798ddbf05227f5778f9a806987821fd4d3eacd6f37c946ac61a9c64fb79ae4b84daaec12354158835a3 + languageName: node + linkType: hard + "@types/parse-json@npm:^4.0.0": version: 4.0.0 resolution: "@types/parse-json@npm:4.0.0" @@ -5314,6 +5390,13 @@ __metadata: languageName: node linkType: hard +"acorn-walk@npm:^8.1.1": + version: 8.3.1 + resolution: "acorn-walk@npm:8.3.1" + checksum: 64187f1377afcba01ec6a57950e3f6a31fff50e429cdb9c9ab2c24343375e711f0d552e5fce5b6ecf21f754566e7526b6d79e4da80bd83c7ad15644d285b2ad5 + languageName: node + linkType: hard + "acorn@npm:^8.1.0, acorn@npm:^8.5.0, acorn@npm:^8.7.1, acorn@npm:^8.8.1": version: 8.8.2 resolution: "acorn@npm:8.8.2" @@ -5323,7 +5406,7 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.8.2": +"acorn@npm:^8.4.1, acorn@npm:^8.8.2": version: 8.11.3 resolution: "acorn@npm:8.11.3" bin: @@ -5709,6 +5792,13 @@ __metadata: languageName: node linkType: hard +"arg@npm:^4.1.0": + version: 4.1.3 + resolution: "arg@npm:4.1.3" + checksum: 969b491082f20cad166649fa4d2073ea9e974a4e5ac36247ca23d2e5a8b3cb12d60e9ff70a8acfe26d76566c71fd351ee5e6a9a6595157eb36f92b1fd64e1599 + languageName: node + linkType: hard + "argparse@npm:^1.0.7": version: 1.0.10 resolution: "argparse@npm:1.0.10" @@ -7148,6 +7238,13 @@ __metadata: languageName: node linkType: hard +"create-require@npm:^1.1.0": + version: 1.1.1 + resolution: "create-require@npm:1.1.1" + checksum: a9a1503d4390d8b59ad86f4607de7870b39cad43d929813599a23714831e81c520bddf61bcdd1f8e30f05fd3a2b71ae8538e946eb2786dc65c2bbc520f692eff + languageName: node + linkType: hard + "cross-env@npm:7.0.3": version: 7.0.3 resolution: "cross-env@npm:7.0.3" @@ -7811,6 +7908,13 @@ __metadata: languageName: node linkType: hard +"diff@npm:^4.0.1": + version: 4.0.2 + resolution: "diff@npm:4.0.2" + checksum: ec09ec2101934ca5966355a229d77afcad5911c92e2a77413efda5455636c4cf2ce84057e2d7715227a2eeeda04255b849bd3ae3a4dd22eb22e86e76456df069 + languageName: node + linkType: hard + "dir-glob@npm:^3.0.1": version: 3.0.1 resolution: "dir-glob@npm:3.0.1" @@ -7993,6 +8097,13 @@ __metadata: languageName: node linkType: hard +"dotenv@npm:*, dotenv@npm:^16.3.1": + version: 16.3.1 + resolution: "dotenv@npm:16.3.1" + checksum: dbb778237ef8750e9e3cd1473d3c8eaa9cc3600e33a75c0e36415d0fa0848197f56c3800f77924c70e7828f0b03896818cd52f785b07b9ad4d88dba73fbba83f + languageName: node + linkType: hard + "dotenv@npm:^16.0.3": version: 16.0.3 resolution: "dotenv@npm:16.0.3" @@ -9333,7 +9444,7 @@ __metadata: languageName: node linkType: hard -"fsevents@npm:^2.3.2, fsevents@npm:~2.3.2": +"fsevents@npm:2.3.2, fsevents@npm:^2.3.2, fsevents@npm:~2.3.2": version: 2.3.2 resolution: "fsevents@npm:2.3.2" dependencies: @@ -9343,7 +9454,7 @@ __metadata: languageName: node linkType: hard -"fsevents@patch:fsevents@npm%3A^2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin": +"fsevents@patch:fsevents@npm%3A2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A^2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin": version: 2.3.2 resolution: "fsevents@patch:fsevents@npm%3A2.3.2#optional!builtin::version=2.3.2&hash=df0bf1" dependencies: @@ -12099,7 +12210,7 @@ __metadata: languageName: node linkType: hard -"make-error@npm:1.x": +"make-error@npm:1.x, make-error@npm:^1.1.1": version: 1.3.6 resolution: "make-error@npm:1.3.6" checksum: b86e5e0e25f7f777b77fabd8e2cbf15737972869d852a22b7e73c17623928fccb826d8e46b9951501d3f20e51ad74ba8c59ed584f610526a48f8ccf88aaec402 @@ -13392,6 +13503,42 @@ __metadata: languageName: node linkType: hard +"playwright-core@npm:1.40.1": + version: 1.40.1 + resolution: "playwright-core@npm:1.40.1" + bin: + playwright-core: cli.js + checksum: b8945a5eec68a2772be537219c81f74b8fbd1545650c908c41fdcb2b9eb40f488968d1fb59c61ca58afb923af1e390dd621bba3099521fcd3d9a63ef3645a203 + languageName: node + linkType: hard + +"playwright-studio@workspace:frontend/testing/playwright": + version: 0.0.0-use.local + resolution: "playwright-studio@workspace:frontend/testing/playwright" + dependencies: + "@playwright/test": "npm:^1.40.1" + "@types/dotenv": "npm:^8.2.0" + "@types/node": "npm:^20.10.5" + dotenv: "npm:^16.3.1" + ts-node: "npm:^10.9.2" + languageName: unknown + linkType: soft + +"playwright@npm:1.40.1": + version: 1.40.1 + resolution: "playwright@npm:1.40.1" + dependencies: + fsevents: "npm:2.3.2" + playwright-core: "npm:1.40.1" + dependenciesMeta: + fsevents: + optional: true + bin: + playwright: cli.js + checksum: 95cf79f1574accbff18fac4b876aa3811640314ee74cf4cd1c249398cd49824e55dc664039342a18d2d2fa4f5ad460ce91a97cb15942ab5d71c8ab04f442dc32 + languageName: node + linkType: hard + "postcss-calc@npm:^9.0.0": version: 9.0.1 resolution: "postcss-calc@npm:9.0.1" @@ -16415,6 +16562,44 @@ __metadata: languageName: node linkType: hard +"ts-node@npm:^10.9.2": + version: 10.9.2 + resolution: "ts-node@npm:10.9.2" + dependencies: + "@cspotcode/source-map-support": "npm:^0.8.0" + "@tsconfig/node10": "npm:^1.0.7" + "@tsconfig/node12": "npm:^1.0.7" + "@tsconfig/node14": "npm:^1.0.0" + "@tsconfig/node16": "npm:^1.0.2" + acorn: "npm:^8.4.1" + acorn-walk: "npm:^8.1.1" + arg: "npm:^4.1.0" + create-require: "npm:^1.1.0" + diff: "npm:^4.0.1" + make-error: "npm:^1.1.1" + v8-compile-cache-lib: "npm:^3.0.1" + yn: "npm:3.1.1" + peerDependencies: + "@swc/core": ">=1.2.50" + "@swc/wasm": ">=1.2.50" + "@types/node": "*" + typescript: ">=2.7" + peerDependenciesMeta: + "@swc/core": + optional: true + "@swc/wasm": + optional: true + bin: + ts-node: dist/bin.js + ts-node-cwd: dist/bin-cwd.js + ts-node-esm: dist/bin-esm.js + ts-node-script: dist/bin-script.js + ts-node-transpile-only: dist/bin-transpile.js + ts-script: dist/bin-script-deprecated.js + checksum: a91a15b3c9f76ac462f006fa88b6bfa528130dcfb849dd7ef7f9d640832ab681e235b8a2bc58ecde42f72851cc1d5d4e22c901b0c11aa51001ea1d395074b794 + languageName: node + linkType: hard + "tsconfig-paths@npm:^3.15.0": version: 3.15.0 resolution: "tsconfig-paths@npm:3.15.0" @@ -16686,6 +16871,13 @@ __metadata: languageName: node linkType: hard +"undici-types@npm:~5.26.4": + version: 5.26.5 + resolution: "undici-types@npm:5.26.5" + checksum: 0097779d94bc0fd26f0418b3a05472410408877279141ded2bd449167be1aed7ea5b76f756562cb3586a07f251b90799bab22d9019ceba49c037c76445f7cddd + languageName: node + linkType: hard + "unicode-canonical-property-names-ecmascript@npm:^2.0.0": version: 2.0.0 resolution: "unicode-canonical-property-names-ecmascript@npm:2.0.0" @@ -16941,6 +17133,13 @@ __metadata: languageName: unknown linkType: soft +"v8-compile-cache-lib@npm:^3.0.1": + version: 3.0.1 + resolution: "v8-compile-cache-lib@npm:3.0.1" + checksum: 88d3423a52b6aaf1836be779cab12f7016d47ad8430dffba6edf766695e6d90ad4adaa3d8eeb512cc05924f3e246c4a4ca51e089dccf4402caa536b5e5be8961 + languageName: node + linkType: hard + "v8-to-istanbul@npm:^9.0.1": version: 9.1.0 resolution: "v8-to-istanbul@npm:9.1.0" @@ -17568,6 +17767,13 @@ __metadata: languageName: node linkType: hard +"yn@npm:3.1.1": + version: 3.1.1 + resolution: "yn@npm:3.1.1" + checksum: 2c487b0e149e746ef48cda9f8bad10fc83693cd69d7f9dcd8be4214e985de33a29c9e24f3c0d6bcf2288427040a8947406ab27f7af67ee9456e6b84854f02dd6 + languageName: node + linkType: hard + "yocto-queue@npm:^0.1.0": version: 0.1.0 resolution: "yocto-queue@npm:0.1.0"