Skip to content

Commit e723f56

Browse files
authored
chore(test): adding playwright e2e test (#318)
* chore(test): adding playwright e2e test Signed-off-by: Daniel Villanueva <[email protected]> * chore(test): adding yarn.lock file Signed-off-by: Daniel Villanueva <[email protected]> * chore(test): update vitest.config.js Signed-off-by: Daniel Villanueva <[email protected]> * chore(test): include checking existence of RH SSO extension Signed-off-by: Daniel Villanueva <[email protected]> --------- Signed-off-by: Daniel Villanueva <[email protected]>
1 parent 7f3290d commit e723f56

File tree

9 files changed

+753
-16
lines changed

9 files changed

+753
-16
lines changed

.eslintrc.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@
1414
"parserOptions": {
1515
"ecmaVersion": 12,
1616
"sourceType": "module",
17-
"project": "./tsconfig.json"
17+
"project": [
18+
"./tsconfig.json",
19+
"tests/tsconfig.json"
20+
]
1821
},
1922
"plugins": [
2023
"@typescript-eslint"
@@ -25,6 +28,7 @@
2528
"builtin/**",
2629
"__mocks__",
2730
"coverage",
31+
"playwright.config.ts",
2832
"rollup.config.js",
2933
"vitest.config.js",
3034
"vite.config.js"

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,6 @@ dist
99
yarn-error.log
1010
builtin
1111
assets
12+
tests/**/output
13+
test-results
14+

package.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,20 +138,26 @@
138138
"desk:build": "ts-node-esm ./scripts/run.mts build",
139139
"desk:prepare": "ts-node-esm ./scripts/run.mts prepare",
140140
"desk:run": "ts-node-esm ./scripts/run.mts run",
141-
"test": "vitest run --coverage --passWithNoTests"
141+
"test": "vitest run --coverage --passWithNoTests",
142+
"test:e2e:setup": "xvfb-maybe --auto-servernum --server-args='-screen 0 1280x960x24' --",
143+
"test:e2e": "npm run test:e2e:setup npx playwright test tests/src"
142144
},
143145
"dependencies": {
144146
"@redhat-developer/rhaccm-client": "^0.0.1"
145147
},
146148
"devDependencies": {
147149
"@podman-desktop/api": "1.12.0",
148-
"@types/node": "^20.16.7",
150+
"@playwright/test": "^1.47.1",
151+
"@podman-desktop/tests-playwright": "next",
152+
"@types/node": "^20.16.5",
149153
"@typescript-eslint/eslint-plugin": "^5.55.0",
150154
"@typescript-eslint/parser": "^5.55.0",
151155
"@vitest/coverage-v8": "^2.0.5",
152156
"byline": "^5.0.0",
153157
"compare-versions": "^6.1.1",
154158
"copyfiles": "^2.4.1",
159+
"cross-env": "7.0.3",
160+
"electron": "^32.1.1",
155161
"eslint": "^8.57.1",
156162
"got": "^14.4.2",
157163
"hasha": "^6.0.0",
@@ -163,6 +169,7 @@
163169
"vite": "^5.4.8",
164170
"vitest": "^2.0.5",
165171
"which": "^4.0.0",
172+
"xvfb-maybe": "^0.2.1",
166173
"zip-local": "^0.3.5"
167174
},
168175
"extensionDependencies": [

playwright.config.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**********************************************************************
2+
* Copyright (C) 2024 Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
***********************************************************************/
18+
19+
import { defineConfig, devices } from '@playwright/test';
20+
21+
export default defineConfig({
22+
outputDir: 'tests/output/',
23+
workers: 1,
24+
timeout: 60000,
25+
26+
reporter: [
27+
['list'],
28+
['junit', { outputFile: 'tests/output/junit-results.xml' }],
29+
['json', { outputFile: 'tests/output/json-results.json' }],
30+
['html', { open: 'never', outputFolder: 'tests/playwright/output/html-results' }],
31+
],
32+
33+
projects: [
34+
{
35+
name: 'chromium',
36+
use: {
37+
...devices['Desktop Chrome'],
38+
},
39+
},
40+
],
41+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**********************************************************************
2+
* Copyright (C) 2024 Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
***********************************************************************/
18+
19+
import type { Page } from '@playwright/test';
20+
import { ExtensionDetailsPage } from '@podman-desktop/tests-playwright';
21+
22+
export class OpenShiftLocalExtensionPage extends ExtensionDetailsPage {
23+
constructor(page: Page) {
24+
super(page, 'Red Hat OpenShift Local Extension');
25+
}
26+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/**********************************************************************
2+
* Copyright (C) 2024 Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
***********************************************************************/
18+
19+
import type { NavigationBar } from '@podman-desktop/tests-playwright';
20+
import { expect as playExpect, ExtensionCardPage, RunnerOptions, test } from '@podman-desktop/tests-playwright';
21+
22+
import { OpenShiftLocalExtensionPage } from './model/pages/openshift-local-extension-page';
23+
24+
let extensionInstalled = false;
25+
let extensionCard: ExtensionCardPage;
26+
const imageName = 'ghcr.io/crc-org/crc-extension:latest';
27+
const extensionLabelCrc = 'redhat.openshift-local';
28+
const extensionLabelNameCrc = 'openshift-local';
29+
const extensionLabelAuthentication = 'redhat.redhat-authentication';
30+
const extensionLabelNameAuthentication = 'redhat-authentication';
31+
const activeExtensionStatus = 'ACTIVE';
32+
const disabledExtensionStatus = 'DISABLED';
33+
const skipInstallation = process.env.SKIP_INSTALLATION ? process.env.SKIP_INSTALLATION : false;
34+
35+
test.use({
36+
runnerOptions: new RunnerOptions({ customFolder: 'crc-tests-pd', autoUpdate: false, autoCheckUpdates: false }),
37+
});
38+
test.beforeAll(async ({ runner, page, welcomePage }) => {
39+
runner.setVideoAndTraceName('crc-e2e');
40+
await welcomePage.handleWelcomePage(true);
41+
extensionCard = new ExtensionCardPage(page, extensionLabelNameCrc, extensionLabelCrc);
42+
});
43+
44+
test.afterAll(async ({ runner }) => {
45+
await runner.close();
46+
});
47+
48+
test.describe.serial('Red Hat OpenShift Local extension verification', () => {
49+
test.describe.serial('Red Hat OpenShift Local extension installation', () => {
50+
// PR check builds extension locally and so it is available already
51+
test('Go to extensions and check if extension is already installed', async ({ navigationBar }) => {
52+
const extensions = await navigationBar.openExtensions();
53+
if (await extensions.extensionIsInstalled(extensionLabelCrc)) {
54+
extensionInstalled = true;
55+
}
56+
});
57+
58+
// we want to skip removing of the extension when we are running tests from PR check
59+
test('Uninstall previous version of crc extension', async ({ navigationBar }) => {
60+
test.skip(!extensionInstalled || !!skipInstallation);
61+
test.setTimeout(60000);
62+
await removeExtension(navigationBar);
63+
});
64+
65+
// we want to install extension from OCI image (usually using latest tag) after new code was added to the codebase
66+
// and extension was published already
67+
test('Extension can be installed using OCI image', async ({ navigationBar }) => {
68+
test.skip(extensionInstalled && !skipInstallation);
69+
test.setTimeout(200000);
70+
const extensions = await navigationBar.openExtensions();
71+
await extensions.installExtensionFromOCIImage(imageName);
72+
await playExpect(extensionCard.card).toBeVisible();
73+
});
74+
75+
test('Extension (card) is installed, present and active', async ({ navigationBar }) => {
76+
const extensions = await navigationBar.openExtensions();
77+
await playExpect.poll(async () =>
78+
await extensions.extensionIsInstalled(extensionLabelCrc), { timeout: 30000 },
79+
).toBeTruthy();
80+
const extensionCard = await extensions.getInstalledExtension(extensionLabelNameCrc, extensionLabelCrc);
81+
await playExpect(extensionCard.status).toHaveText(activeExtensionStatus);
82+
});
83+
84+
test('Extension\'s dependency, Red Hat Authentication, (card) is installed, present and active', async ({ navigationBar }) => {
85+
const extensions = await navigationBar.openExtensions();
86+
await playExpect.poll(async () =>
87+
await extensions.extensionIsInstalled(extensionLabelAuthentication), { timeout: 30000 },
88+
).toBeTruthy();
89+
const extensionCard = await extensions.getInstalledExtension(extensionLabelNameAuthentication, extensionLabelAuthentication);
90+
await playExpect(extensionCard.status).toHaveText(activeExtensionStatus);
91+
});
92+
93+
test('Extension\'s details show correct status, no error', async ({ page,navigationBar }) => {
94+
const extensions = await navigationBar.openExtensions();
95+
const extensionCard = await extensions.getInstalledExtension(extensionLabelNameCrc, extensionLabelCrc);
96+
await extensionCard.openExtensionDetails('Red Hat Authentication');
97+
const details = new OpenShiftLocalExtensionPage(page);
98+
await playExpect(details.heading).toBeVisible();
99+
await playExpect(details.status).toHaveText(activeExtensionStatus);
100+
const errorTab = details.tabs.getByRole('button', { name: 'Error' });
101+
// we would like to propagate the error's stack trace into test failure message
102+
let stackTrace = '';
103+
if ((await errorTab.count()) > 0) {
104+
await details.activateTab('Error');
105+
stackTrace = await details.errorStackTrace.innerText();
106+
}
107+
await playExpect(errorTab, `Error Tab was present with stackTrace: ${stackTrace}`).not.toBeVisible();
108+
});
109+
});
110+
111+
test.describe.serial('Red Hat OpenShift Local extension handling', () => {
112+
test('Extension can be disabled', async ({ navigationBar }) => {
113+
const extensions = await navigationBar.openExtensions();
114+
playExpect(await extensions.extensionIsInstalled(extensionLabelCrc)).toBeTruthy();
115+
const extensionCard = await extensions.getInstalledExtension(extensionLabelNameCrc, extensionLabelCrc);
116+
await playExpect(extensionCard.status).toHaveText(activeExtensionStatus);
117+
await extensionCard.disableExtension();
118+
await playExpect(extensionCard.status).toHaveText(disabledExtensionStatus);
119+
});
120+
121+
test('Extension can be re-enabled correctly', async ({ navigationBar }) => {
122+
const extensions = await navigationBar.openExtensions();
123+
playExpect(await extensions.extensionIsInstalled(extensionLabelCrc)).toBeTruthy();
124+
const extensionCard = await extensions.getInstalledExtension(extensionLabelNameCrc, extensionLabelCrc);
125+
await playExpect(extensionCard.status).toHaveText(disabledExtensionStatus);
126+
await extensionCard.enableExtension();
127+
await playExpect(extensionCard.status).toHaveText(activeExtensionStatus);
128+
});
129+
});
130+
131+
test('OpenShift Local extension can be removed', async ({ navigationBar }) => {
132+
await removeExtension(navigationBar);
133+
});
134+
});
135+
136+
async function removeExtension(navBar: NavigationBar): Promise<void> {
137+
const extensions = await navBar.openExtensions();
138+
const extensionCard = await extensions.getInstalledExtension(extensionLabelNameCrc, extensionLabelCrc);
139+
await extensionCard.disableExtension();
140+
await extensionCard.removeExtension();
141+
await playExpect.poll(async () => await extensions.extensionIsInstalled(extensionLabelCrc), { timeout: 15000 }).toBeFalsy();
142+
}

tests/tsconfig.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"compilerOptions": {
3+
"strictNullChecks": true,
4+
"lib": [ "ES2017", "webworker" ],
5+
"module": "esnext",
6+
"target": "esnext",
7+
"sourceMap": true,
8+
"rootDir": "src",
9+
"outDir": "dist",
10+
"skipLibCheck": true,
11+
"types": [ "node" ],
12+
"allowSyntheticDefaultImports": true,
13+
"moduleResolution": "Node",
14+
"esModuleInterop": true
15+
}
16+
}

vitest.config.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ import path from 'node:path';
2121
const config = {
2222
test: {
2323
include: ['**/*.{test,spec}.?(c|m)[jt]s?(x)'],
24+
exclude: ['tests/**', '**/builtin/**',
25+
'**/node_modules/**',
26+
'**/dist/**',
27+
'**/.{idea,git,cache,output,temp,cdix}/**',
28+
'**/{.electron-builder,babel,changelog,docusaurus,jest,postcss,prettier,rollup,svelte,tailwind,vite,vitest*,webpack}.config.*',],
2429
coverage: {
2530
provider: 'v8',
2631
reporter: ['lcov', 'text'],

0 commit comments

Comments
 (0)