From 5e4768cbc5766b1dd34a5fcc43233bc28da2f374 Mon Sep 17 00:00:00 2001 From: Harmen Wessels <97173058+harmen-xb@users.noreply.github.com> Date: Wed, 15 Nov 2023 19:42:08 +0100 Subject: [PATCH] First setup of e2e testing with playwright including github workflow. --- .github/workflows/build-and-test.yml | 20 +++++++- .gitignore | 4 +- e2e-tests/package.json | 21 ++++++++ e2e-tests/playwright.config.ts | 33 +++++++++++++ e2e-tests/src/page-objects/crossmodel-app.ts | 6 +++ .../src/page-objects/crossmodel-workspace.ts | 6 +++ .../sample-workspace/example-entity.cm | 14 ++++++ e2e-tests/src/tests/xmodel-app.test.ts | 49 +++++++++++++++++++ .../src/tests/xmodel-explorer-view.test.ts | 30 ++++++++++++ e2e-tests/tsconfig.json | 10 ++++ package.json | 3 +- tsconfig.eslint.json | 3 +- tsconfig.json | 3 ++ yarn.lock | 36 +++++++++++++- 14 files changed, 232 insertions(+), 6 deletions(-) create mode 100644 e2e-tests/package.json create mode 100644 e2e-tests/playwright.config.ts create mode 100644 e2e-tests/src/page-objects/crossmodel-app.ts create mode 100644 e2e-tests/src/page-objects/crossmodel-workspace.ts create mode 100644 e2e-tests/src/resources/sample-workspace/example-entity.cm create mode 100644 e2e-tests/src/tests/xmodel-app.test.ts create mode 100644 e2e-tests/src/tests/xmodel-explorer-view.test.ts create mode 100644 e2e-tests/tsconfig.json diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 38192e20..4db29e49 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -53,17 +53,33 @@ jobs: JEST_JUNIT_OUTPUT_NAME: unit-test-results-${{ runner.os }}.xml # Upload Test Results (The different files for the OSes will end up in the same artifact). - - name: Upload Test Results + - name: Upload Unit Test Results if: always() uses: actions/upload-artifact@v3 with: name: unit-test-tesults # Include the unit-test-results folders (which is in the root of the workspace). path: unit-test-results + retention-days: 30 + + # Run PlayWright tests, only on Linux container. + - name: Install Playwright Browsers + if: runner.os == 'Linux' + run: yarn --cwd ./e2e-tests/ playwright install --with-deps + - name: Run Playwright tests + if: runner.os == 'Linux' + run: yarn --cwd ./e2e-tests/ test + - name: Upload PlayWrite test report + if: always() && runner.os == 'Linux' + uses: actions/upload-artifact@v3 + with: + name: playwright-report + path: e2e-tests/playwright-report/ + retention-days: 30 # Run lint only on Linux (since it only makes sense to run it once, and linux is the fastest). - name: Lint - if: runner.os == 'Linux' + if: always() && runner.os == 'Linux' run: yarn lint # Publish a test report using the test result files published in the previous step (executed per OS). diff --git a/.gitignore b/.gitignore index 001d71f2..3f110d2b 100644 --- a/.gitignore +++ b/.gitignore @@ -14,5 +14,7 @@ bin .ignored_out dist *.port -**/unit-test-results +**/*test-results **/coverage +**/playwright-report +**/playwright/.cache \ No newline at end of file diff --git a/e2e-tests/package.json b/e2e-tests/package.json new file mode 100644 index 00000000..70a2cfbe --- /dev/null +++ b/e2e-tests/package.json @@ -0,0 +1,21 @@ +{ + "name": "crossmodel-e2e-tests", + "version": "0.0.0", + "private": true, + "license": "AGPL-3.0-or-later", + "author": { + "name": "CrossBreeze", + "email": "devops@crossbreeze.nl" + }, + "scripts": { + "build": "tsc --incremental && npx playwright install chromium", + "clean": "rimraf lib tsconfig.tsbuildinfo", + "lint": "eslint -c ../.eslintrc.js --ext .ts ./src", + "prepare": "yarn clean && yarn build && yarn lint", + "test": "yarn playwright test" + }, + "dependencies": { + "@playwright/test": "^1.37.1", + "@theia/playwright": "1.43.1" + } +} diff --git a/e2e-tests/playwright.config.ts b/e2e-tests/playwright.config.ts new file mode 100644 index 00000000..ed185c29 --- /dev/null +++ b/e2e-tests/playwright.config.ts @@ -0,0 +1,33 @@ +/******************************************************************************** + * Copyright (c) 2023 CrossBreeze. + ********************************************************************************/ +import { defineConfig } from '@playwright/test'; + +export default defineConfig({ + testDir: './src/tests', + testMatch: ['**/*.test.ts'], + workers: process.env.CI ? 1 : 2, + retries: process.env.CI ? 1 : 0, + // The number of times to repeat each test, useful for debugging flaky tests + repeatEach: 1, + // Timeout for each test in milliseconds. + timeout: 30 * 1000, + use: { + baseURL: 'http://localhost:3000', + browserName: 'chromium', + screenshot: 'only-on-failure', + viewport: { width: 1920, height: 1080 } + }, + snapshotDir: './src/tests/snapshots', + expect: { + toMatchSnapshot: { threshold: 0.01 } + }, + preserveOutput: 'failures-only', + reporter: process.env.CI ? [['list'], ['html'], ['github']] : [['list'], ['html']], + /* Run your local dev server before starting the tests */ + webServer: { + command: 'yarn --cwd ../../ start:browser', + url: 'http://localhost:3000', + reuseExistingServer: !process.env.CI + } +}); diff --git a/e2e-tests/src/page-objects/crossmodel-app.ts b/e2e-tests/src/page-objects/crossmodel-app.ts new file mode 100644 index 00000000..1eae5305 --- /dev/null +++ b/e2e-tests/src/page-objects/crossmodel-app.ts @@ -0,0 +1,6 @@ +/******************************************************************************** + * Copyright (c) 2023 CrossBreeze. + ********************************************************************************/ +import { TheiaApp } from '@theia/playwright'; + +export class CrossModelApp extends TheiaApp {} diff --git a/e2e-tests/src/page-objects/crossmodel-workspace.ts b/e2e-tests/src/page-objects/crossmodel-workspace.ts new file mode 100644 index 00000000..faaa1155 --- /dev/null +++ b/e2e-tests/src/page-objects/crossmodel-workspace.ts @@ -0,0 +1,6 @@ +/******************************************************************************** + * Copyright (c) 2023 CrossBreeze. + ********************************************************************************/ +import { TheiaWorkspace } from '@theia/playwright'; + +export class CrossModelWorkspace extends TheiaWorkspace {} diff --git a/e2e-tests/src/resources/sample-workspace/example-entity.cm b/e2e-tests/src/resources/sample-workspace/example-entity.cm new file mode 100644 index 00000000..48e512a8 --- /dev/null +++ b/e2e-tests/src/resources/sample-workspace/example-entity.cm @@ -0,0 +1,14 @@ +entity: + id: "ExampleEntity" + name: "Example Entity" + description: "An example entity" + attributes: + - id: "Id" + name: "Id" + datatype: "Integer" + - id: "FirstAttribute" + name: "First Attribute" + datatype: "Varchar" + - id: "SecondAttribute" + name: "Second Attribute" + datatype: "Varchar" \ No newline at end of file diff --git a/e2e-tests/src/tests/xmodel-app.test.ts b/e2e-tests/src/tests/xmodel-app.test.ts new file mode 100644 index 00000000..db411c5c --- /dev/null +++ b/e2e-tests/src/tests/xmodel-app.test.ts @@ -0,0 +1,49 @@ +/******************************************************************************** + * Copyright (c) 2023 CrossBreeze. + ********************************************************************************/ +import { test, expect, Page } from '@playwright/test'; +import { CrossModelApp } from '../page-objects/crossmodel-app'; + +let page: Page; +let app: CrossModelApp; + +test.beforeAll(async ({ browser }) => { + page = await browser.newPage(); + app = await CrossModelApp.loadApp(page, CrossModelApp); +}); + +test.describe('CrossModel is visible', () => { + test('should show main content panel', async () => { + expect(await app.isMainContentPanelVisible()).toBe(true); + }); +}); + +// test('Create new entity from workspace toolbar', async ({ page }) => { +// const exampleEntityname = 'ExampleEntity'; +// const exampleFileName = '${exampleEntityname}.entity.cm'; + +// await page.goto('http://localhost:3000/#/c:/git/GitHub/crossmodel/examples/yaml-example'); +// await page.getByText('View').click(); +// await page.getByText('Explorer').nth(3).click(); + +// // Store the location to the files list. +// const explorerFilesList = await page.locator('#files'); + +// // The example file should not be in the workspace yet. +// await expect(explorerFilesList).not.toContainText(exampleFileName); + +// // Create the entity. +// await page.getByTitle('New Entity...').click(); +// await page.getByPlaceholder('New Entity').fill(exampleEntityname); +// await page.getByRole('button', { name: 'OK' }).click(); + +// // Expect the new file to be in the files list. +// await expect(explorerFilesList.getByText(exampleFileName)).toBeVisible(); + +// // Now we delete the file. +// await explorerFilesList.getByText(exampleFileName).click({ +// button: 'right' +// }); +// await page.getByText('Delete').first().click(); +// await page.getByRole('button', { name: 'OK' }).click(); +// }); diff --git a/e2e-tests/src/tests/xmodel-explorer-view.test.ts b/e2e-tests/src/tests/xmodel-explorer-view.test.ts new file mode 100644 index 00000000..b234cd04 --- /dev/null +++ b/e2e-tests/src/tests/xmodel-explorer-view.test.ts @@ -0,0 +1,30 @@ +/******************************************************************************** + * Copyright (c) 2023 CrossBreeze. + ********************************************************************************/ +import { test, expect, Page } from '@playwright/test'; +import { CrossModelApp } from '../page-objects/crossmodel-app'; +import { CrossModelWorkspace } from '../page-objects/crossmodel-workspace'; +import { TheiaExplorerView } from '@theia/playwright'; + +let page: Page; +let app: CrossModelApp; +let explorer: TheiaExplorerView; + +test.describe('CrossModel Explorer View', () => { + test.beforeAll(async ({ browser }) => { + page = await browser.newPage(); + const ws = new CrossModelWorkspace(['src/resources/sample-workspace']); + app = await CrossModelApp.load(page, ws); + explorer = await app.openView(TheiaExplorerView); + await explorer.waitForVisibleFileNodes(); + }); + + test('should open context menu on "example-entity.cm"', async () => { + const file = await explorer.getFileStatNodeByLabel('example-entity.cm'); + const menu = await file.openContextMenu(); + expect(await menu.isOpen()).toBe(true); + + const menuItems = await menu.visibleMenuItems(); + expect(menuItems).toContain('Open With...'); + }); +}); diff --git a/e2e-tests/tsconfig.json b/e2e-tests/tsconfig.json new file mode 100644 index 00000000..7c22702e --- /dev/null +++ b/e2e-tests/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../configs/base.tsconfig.json", + "compilerOptions": { + "composite": true, + "baseUrl": ".", + "rootDir": ".", + "outDir": "lib" + }, + "include": ["src/tests", "src/page-objects", "src/resources"] +} diff --git a/package.json b/package.json index 77f79ed6..00478cf7 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "workspaces": [ "packages/*", "applications/*", - "extensions/*" + "extensions/*", + "e2e-tests" ], "scripts": { "build": "lerna run build", diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json index c29ef763..16b67d92 100644 --- a/tsconfig.eslint.json +++ b/tsconfig.eslint.json @@ -10,6 +10,7 @@ "applications/*/src", "applications/*/scripts", "extensions/*/src", - "extensions/*/test" + "extensions/*/test", + "e2e-tests/src/" ] } diff --git a/tsconfig.json b/tsconfig.json index 6911c607..39b92adc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -35,6 +35,9 @@ }, { "path": "packages/protocol" + }, + { + "path": "e2e-tests" } ] } diff --git a/yarn.lock b/yarn.lock index b63157ab..ed1b89a0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1914,6 +1914,13 @@ picocolors "^1.0.0" tslib "^2.6.0" +"@playwright/test@^1.37.1": + version "1.39.0" + resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.39.0.tgz#d10ba8e38e44104499e25001945f07faa9fa91cd" + integrity sha512-3u1iFqgzl7zr004bGPYiN/5EZpRUSFddQBra8Rqll5N0/vfpqlP9I9EXqAoGacuAbX6c9Ulg/Cjqglp5VkK6UQ== + dependencies: + playwright "1.39.0" + "@popperjs/core@^2.11.8": version "2.11.8" resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" @@ -2423,6 +2430,14 @@ "@theia/request" "1.43.1" semver "^7.5.4" +"@theia/playwright@1.43.1": + version "1.43.1" + resolved "https://registry.yarnpkg.com/@theia/playwright/-/playwright-1.43.1.tgz#283b60c16b20b2ca4f3d206cb1f3df3addb41677" + integrity sha512-g7CVN9rXJCRXN69m/GEo2VfAnvOupFgtMyIwS9VApmDbUI6oaVu159ekIiFxPI2pIReIbOsaeYlaJm7EWIPxHw== + dependencies: + "@playwright/test" "^1.37.1" + fs-extra "^9.0.8" + "@theia/plugin-ext-vscode@1.43.1": version "1.43.1" resolved "https://registry.yarnpkg.com/@theia/plugin-ext-vscode/-/plugin-ext-vscode-1.43.1.tgz#4921f70116af1953af6bd93b9d36e9fa0c316166" @@ -7450,7 +7465,7 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^9.0.0, fs-extra@^9.0.1: +fs-extra@^9.0.0, fs-extra@^9.0.1, fs-extra@^9.0.8: version "9.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== @@ -7488,6 +7503,11 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== +fsevents@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + fsevents@^2.3.2, fsevents@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" @@ -11221,6 +11241,20 @@ pkginfo@0.4.1: resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.4.1.tgz#b5418ef0439de5425fc4995042dced14fb2a84ff" integrity sha512-8xCNE/aT/EXKenuMDZ+xTVwkT8gsoHN2z/Q29l80u0ppGEXVvsKRzNMbtKhg8LS8k1tJLAHHylf6p4VFmP6XUQ== +playwright-core@1.39.0: + version "1.39.0" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.39.0.tgz#efeaea754af4fb170d11845b8da30b2323287c63" + integrity sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw== + +playwright@1.39.0: + version "1.39.0" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.39.0.tgz#184c81cd6478f8da28bcd9e60e94fcebf566e077" + integrity sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw== + dependencies: + playwright-core "1.39.0" + optionalDependencies: + fsevents "2.3.2" + plist@^3.0.1, plist@^3.0.4: version "3.1.0" resolved "https://registry.yarnpkg.com/plist/-/plist-3.1.0.tgz#797a516a93e62f5bde55e0b9cc9c967f860893c9"