Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(playwright): setup and config #11836

Merged
merged 60 commits into from
Jan 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
78e90f7
work in progress
framitdavid Dec 8, 2023
d18c5d7
config playwrigth
framitdavid Dec 11, 2023
68d833c
Merge branch 'master' into feat/playwright
framitdavid Dec 11, 2023
407b760
just for testing
framitdavid Dec 11, 2023
5f9d4e9
revert yml to use correct triggers for master
framitdavid Dec 11, 2023
164aa49
revert unreleated changes
framitdavid Dec 11, 2023
3628ce5
removed default test-examples
framitdavid Dec 11, 2023
2b7a99e
created shared auth-context
framitdavid Dec 11, 2023
4a9975f
clean ups and improve paths
framitdavid Dec 11, 2023
5d333e7
make playwright as standalone workspace package
framitdavid Dec 11, 2023
a3d34ec
update git ignore
framitdavid Dec 11, 2023
035e57f
set CI to true within the workflow
framitdavid Dec 12, 2023
864a52a
revert pre-commit
framitdavid Dec 12, 2023
dbd1b1c
delete file that should not be included in git
framitdavid Dec 12, 2023
cd1cf36
Merge branch 'master' into feat/playwright
framitdavid Dec 12, 2023
03e2f5e
added more e2e-tests
framitdavid Dec 13, 2023
5c0cd59
work in progress
framitdavid Dec 14, 2023
706357f
merge master into feature
framitdavid Dec 18, 2023
d4dbf1a
code clean ups and written a setup script
framitdavid Dec 19, 2023
225b620
prettier
framitdavid Dec 19, 2023
139c5a7
removed unsued code
framitdavid Dec 19, 2023
9731d40
removed dotenv package from global package and include within playwirght
framitdavid Dec 19, 2023
b74176a
revert some formatting
framitdavid Dec 19, 2023
2292b89
improved yml file
framitdavid Dec 19, 2023
9c15161
work in progress by extending the test options
framitdavid Dec 19, 2023
0187063
Merge branch 'master' into feat/playwright
framitdavid Dec 19, 2023
a82d2bd
implement support of parameterized tests
framitdavid Dec 19, 2023
295b5c4
added a comment to explain why we extends the test
framitdavid Dec 19, 2023
dcf298a
Merge branch 'master' into feat/playwright
framitdavid Dec 19, 2023
517cfa3
improved README.md
framitdavid Dec 19, 2023
deaeeaf
clean up setup.ts script
framitdavid Dec 19, 2023
7365ace
added support of teardown by deleting the app after test runs
framitdavid Dec 19, 2023
b4b78b6
refactor to keep things within feature-folders
framitdavid Dec 19, 2023
277252e
create script to make access-token to gitea
framitdavid Dec 19, 2023
2bf7aa8
updated the README.md file
framitdavid Dec 19, 2023
a90ebbd
docs
framitdavid Dec 19, 2023
eec4f3e
Merge branch 'master' into feat/playwright
framitdavid Dec 20, 2023
20190ae
updated jest.config to match *.test only
framitdavid Dec 20, 2023
f75813d
some code clean-ups
framitdavid Dec 20, 2023
209cd97
removed @types/node from root package.json
framitdavid Dec 20, 2023
f55cddf
merge master into branch
framitdavid Jan 3, 2024
d50a97b
yarn.lock
framitdavid Jan 3, 2024
010aef3
merge master into feature
framitdavid Jan 8, 2024
392d903
Fixed yarn.lock file
framitdavid Jan 8, 2024
b958fba
use gherkin-like syntax
framitdavid Jan 8, 2024
cf37b32
updated README.md and simplified setup
framitdavid Jan 8, 2024
5224007
revert tests to be like playwright native
framitdavid Jan 10, 2024
d7e3f45
update yml file
framitdavid Jan 10, 2024
c210f44
updated command for running tests
framitdavid Jan 10, 2024
625756e
update yml file
framitdavid Jan 10, 2024
d6fe40e
delete yml for now
framitdavid Jan 10, 2024
a0d96c8
Merge branch 'master' into feat/playwright
framitdavid Jan 10, 2024
d20facf
Merge branch 'master' into feat/playwright
framitdavid Jan 10, 2024
6bf8bab
PR-feedback
framitdavid Jan 12, 2024
b5b163a
Merge branch 'feat/playwright' of https://github.com/Altinn/altinn-st…
framitdavid Jan 12, 2024
f49fc88
Merge branch 'master' into feat/playwright
framitdavid Jan 12, 2024
72bf08d
PR feedback
framitdavid Jan 12, 2024
2f2a8dc
Merge branch 'feat/playwright' of https://github.com/Altinn/altinn-st…
framitdavid Jan 12, 2024
efbb8d7
Merge branch 'master' into feat/playwright
framitdavid Jan 12, 2024
f797050
Clarify that this page is not using the nb/en.json files for language
framitdavid Jan 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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/
2 changes: 1 addition & 1 deletion frontend/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)$',
framitdavid marked this conversation as resolved.
Show resolved Hide resolved
moduleFileExtensions: ['ts', 'tsx', 'js'],
testEnvironment: 'jsdom',
setupFilesAfterEnv: [path.join(__dirname, 'testing/setupTests.ts')],
Expand Down
1 change: 0 additions & 1 deletion frontend/testing/.eslintignore

This file was deleted.

45 changes: 45 additions & 0 deletions frontend/testing/playwright/README.md
Original file line number Diff line number Diff line change
@@ -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=<<your-test-user-username>>
PLAYWRIGHT_PASS=<<your-test-user-password>>
PLAYWRIGHT_DESIGNER_APP_NAME=<<name-of-the-designer-app>>
GITEA_ACCESS_TOKEN=<<generated-gitea-token-by-the-setup-script>>
```

## 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`.
13 changes: 13 additions & 0 deletions frontend/testing/playwright/extenders/testExtend.ts
Original file line number Diff line number Diff line change
@@ -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<ExtendedTestOptions>({
testAppName: [process.env.PLAYWRIGHT_DESIGNER_APP_NAME, { option: true }],
});

const describe = test.describe;
export { describe };
26 changes: 26 additions & 0 deletions frontend/testing/playwright/helpers/BasePage.ts
Original file line number Diff line number Diff line change
@@ -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<Locale, typeof nbTexts | typeof enTexts> = {
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;
}
}
15 changes: 15 additions & 0 deletions frontend/testing/playwright/helpers/Gitea.ts
Original file line number Diff line number Diff line change
@@ -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}`;
}
}
36 changes: 36 additions & 0 deletions frontend/testing/playwright/helpers/RouterRoute.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Environment, StudioEnvironment } from './StudioEnvironment';

type SupportedRoutes = 'altinnLoginPage' | 'dashboard' | 'dashboardCreateApp' | 'editorOverview';

type RouterRoutes = Record<SupportedRoutes, string>;

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}}');
}
}
19 changes: 19 additions & 0 deletions frontend/testing/playwright/helpers/StudioEnvironment.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}
21 changes: 21 additions & 0 deletions frontend/testing/playwright/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
framitdavid marked this conversation as resolved.
Show resolved Hide resolved
"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"
}
}
29 changes: 29 additions & 0 deletions frontend/testing/playwright/pages/CreateServicePage.ts
Original file line number Diff line number Diff line change
@@ -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<void> {
await this.page.goto(this.getRoute('dashboardCreateApp'));
}

public async verifyCreateAppFormPage(): Promise<void> {
await this.page.waitForURL(this.getRoute('dashboardCreateApp'));
}

public async writeAppName(appName: string): Promise<void> {
await this.page.getByLabel(this.textMock('dashboard.name')).fill(appName);
return this.updateAppNameEnv(appName);
}

public async clickOnCreateAppButton(): Promise<void> {
await this.page.getByRole('button', { name: this.textMock('dashboard.create_service_btn') }).click();
}

public async verifyIsNavigatedToOverviewPage(): Promise<void> {
await this.page.waitForURL(this.getRoute('editorOverview'));
}
}
20 changes: 20 additions & 0 deletions frontend/testing/playwright/pages/DashboardPage.ts
Original file line number Diff line number Diff line change
@@ -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<void> {
await this.page.goto(this.getRoute('dashboard'));
}

public async verifyDashboardPage(): Promise<void> {
await this.page.waitForURL(this.getRoute('dashboard'));
}

public async clickOnCreateAppLink(): Promise<void> {
await this.page.getByRole('link', { name: this.textMock('dashboard.new_service') }).click();
}
}
45 changes: 45 additions & 0 deletions frontend/testing/playwright/pages/LoginPage.ts
Original file line number Diff line number Diff line change
@@ -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<string, string> = {
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<void> {
await this.page.goto(this.getRoute('altinnLoginPage'));
}

public async goToGiteaLoginPage(): Promise<void> {
await this.page.getByRole('button', { name: loginPageTexts['login'] }).click();
}

public async writeUsername(username: string): Promise<void> {
return await this.page.getByLabel(loginPageTexts['username']).fill(username);
}

public async writePassword(password: string): Promise<void> {
return await this.page.getByLabel(loginPageTexts['password']).fill(password);
}

public async clickLoginButton(): Promise<void> {
return await this.page.getByRole('button', { name: loginPageTexts['login'] }).click();
}

public async confirmSuccessfulLogin(): Promise<void> {
return this.page.waitForURL(this.getRoute('dashboard'));
}

public async addSessionToSharableStorage() {
return await this.page.context().storageState({ path: this.authStorageFile });
}
}
46 changes: 46 additions & 0 deletions frontend/testing/playwright/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { defineConfig, devices } from '@playwright/test';
import { config } from 'dotenv';
import { ExtendedTestOptions } from './extenders/testExtend';

config();

export default defineConfig<ExtendedTestOptions>({
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',
},
},
],
});

Loading
Loading