Skip to content

Commit

Permalink
chore(test): adding playwright e2e test (#318)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
danivilla9 authored Sep 26, 2024
1 parent 7f3290d commit e723f56
Show file tree
Hide file tree
Showing 9 changed files with 753 additions and 16 deletions.
6 changes: 5 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
"parserOptions": {
"ecmaVersion": 12,
"sourceType": "module",
"project": "./tsconfig.json"
"project": [
"./tsconfig.json",
"tests/tsconfig.json"
]
},
"plugins": [
"@typescript-eslint"
Expand All @@ -25,6 +28,7 @@
"builtin/**",
"__mocks__",
"coverage",
"playwright.config.ts",
"rollup.config.js",
"vitest.config.js",
"vite.config.js"
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ dist
yarn-error.log
builtin
assets
tests/**/output
test-results

11 changes: 9 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -138,20 +138,26 @@
"desk:build": "ts-node-esm ./scripts/run.mts build",
"desk:prepare": "ts-node-esm ./scripts/run.mts prepare",
"desk:run": "ts-node-esm ./scripts/run.mts run",
"test": "vitest run --coverage --passWithNoTests"
"test": "vitest run --coverage --passWithNoTests",
"test:e2e:setup": "xvfb-maybe --auto-servernum --server-args='-screen 0 1280x960x24' --",
"test:e2e": "npm run test:e2e:setup npx playwright test tests/src"
},
"dependencies": {
"@redhat-developer/rhaccm-client": "^0.0.1"
},
"devDependencies": {
"@podman-desktop/api": "1.12.0",
"@types/node": "^20.16.7",
"@playwright/test": "^1.47.1",
"@podman-desktop/tests-playwright": "next",
"@types/node": "^20.16.5",
"@typescript-eslint/eslint-plugin": "^5.55.0",
"@typescript-eslint/parser": "^5.55.0",
"@vitest/coverage-v8": "^2.0.5",
"byline": "^5.0.0",
"compare-versions": "^6.1.1",
"copyfiles": "^2.4.1",
"cross-env": "7.0.3",
"electron": "^32.1.1",
"eslint": "^8.57.1",
"got": "^14.4.2",
"hasha": "^6.0.0",
Expand All @@ -163,6 +169,7 @@
"vite": "^5.4.8",
"vitest": "^2.0.5",
"which": "^4.0.0",
"xvfb-maybe": "^0.2.1",
"zip-local": "^0.3.5"
},
"extensionDependencies": [
Expand Down
41 changes: 41 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**********************************************************************
* Copyright (C) 2024 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/

import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
outputDir: 'tests/output/',
workers: 1,
timeout: 60000,

reporter: [
['list'],
['junit', { outputFile: 'tests/output/junit-results.xml' }],
['json', { outputFile: 'tests/output/json-results.json' }],
['html', { open: 'never', outputFolder: 'tests/playwright/output/html-results' }],
],

projects: [
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
},
},
],
});
26 changes: 26 additions & 0 deletions tests/src/model/pages/openshift-local-extension-page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**********************************************************************
* Copyright (C) 2024 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/

import type { Page } from '@playwright/test';
import { ExtensionDetailsPage } from '@podman-desktop/tests-playwright';

export class OpenShiftLocalExtensionPage extends ExtensionDetailsPage {
constructor(page: Page) {
super(page, 'Red Hat OpenShift Local Extension');
}
}
142 changes: 142 additions & 0 deletions tests/src/openshift-local-extension.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/**********************************************************************
* Copyright (C) 2024 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/

import type { NavigationBar } from '@podman-desktop/tests-playwright';
import { expect as playExpect, ExtensionCardPage, RunnerOptions, test } from '@podman-desktop/tests-playwright';

import { OpenShiftLocalExtensionPage } from './model/pages/openshift-local-extension-page';

let extensionInstalled = false;
let extensionCard: ExtensionCardPage;
const imageName = 'ghcr.io/crc-org/crc-extension:latest';
const extensionLabelCrc = 'redhat.openshift-local';
const extensionLabelNameCrc = 'openshift-local';
const extensionLabelAuthentication = 'redhat.redhat-authentication';
const extensionLabelNameAuthentication = 'redhat-authentication';
const activeExtensionStatus = 'ACTIVE';
const disabledExtensionStatus = 'DISABLED';
const skipInstallation = process.env.SKIP_INSTALLATION ? process.env.SKIP_INSTALLATION : false;

test.use({
runnerOptions: new RunnerOptions({ customFolder: 'crc-tests-pd', autoUpdate: false, autoCheckUpdates: false }),
});
test.beforeAll(async ({ runner, page, welcomePage }) => {
runner.setVideoAndTraceName('crc-e2e');
await welcomePage.handleWelcomePage(true);
extensionCard = new ExtensionCardPage(page, extensionLabelNameCrc, extensionLabelCrc);
});

test.afterAll(async ({ runner }) => {
await runner.close();
});

test.describe.serial('Red Hat OpenShift Local extension verification', () => {
test.describe.serial('Red Hat OpenShift Local extension installation', () => {
// PR check builds extension locally and so it is available already
test('Go to extensions and check if extension is already installed', async ({ navigationBar }) => {
const extensions = await navigationBar.openExtensions();
if (await extensions.extensionIsInstalled(extensionLabelCrc)) {
extensionInstalled = true;
}
});

// we want to skip removing of the extension when we are running tests from PR check
test('Uninstall previous version of crc extension', async ({ navigationBar }) => {
test.skip(!extensionInstalled || !!skipInstallation);
test.setTimeout(60000);
await removeExtension(navigationBar);
});

// we want to install extension from OCI image (usually using latest tag) after new code was added to the codebase
// and extension was published already
test('Extension can be installed using OCI image', async ({ navigationBar }) => {
test.skip(extensionInstalled && !skipInstallation);
test.setTimeout(200000);
const extensions = await navigationBar.openExtensions();
await extensions.installExtensionFromOCIImage(imageName);
await playExpect(extensionCard.card).toBeVisible();
});

test('Extension (card) is installed, present and active', async ({ navigationBar }) => {
const extensions = await navigationBar.openExtensions();
await playExpect.poll(async () =>
await extensions.extensionIsInstalled(extensionLabelCrc), { timeout: 30000 },
).toBeTruthy();
const extensionCard = await extensions.getInstalledExtension(extensionLabelNameCrc, extensionLabelCrc);
await playExpect(extensionCard.status).toHaveText(activeExtensionStatus);
});

test('Extension\'s dependency, Red Hat Authentication, (card) is installed, present and active', async ({ navigationBar }) => {
const extensions = await navigationBar.openExtensions();
await playExpect.poll(async () =>
await extensions.extensionIsInstalled(extensionLabelAuthentication), { timeout: 30000 },
).toBeTruthy();
const extensionCard = await extensions.getInstalledExtension(extensionLabelNameAuthentication, extensionLabelAuthentication);
await playExpect(extensionCard.status).toHaveText(activeExtensionStatus);
});

test('Extension\'s details show correct status, no error', async ({ page,navigationBar }) => {
const extensions = await navigationBar.openExtensions();
const extensionCard = await extensions.getInstalledExtension(extensionLabelNameCrc, extensionLabelCrc);
await extensionCard.openExtensionDetails('Red Hat Authentication');
const details = new OpenShiftLocalExtensionPage(page);
await playExpect(details.heading).toBeVisible();
await playExpect(details.status).toHaveText(activeExtensionStatus);
const errorTab = details.tabs.getByRole('button', { name: 'Error' });
// we would like to propagate the error's stack trace into test failure message
let stackTrace = '';
if ((await errorTab.count()) > 0) {
await details.activateTab('Error');
stackTrace = await details.errorStackTrace.innerText();
}
await playExpect(errorTab, `Error Tab was present with stackTrace: ${stackTrace}`).not.toBeVisible();
});
});

test.describe.serial('Red Hat OpenShift Local extension handling', () => {
test('Extension can be disabled', async ({ navigationBar }) => {
const extensions = await navigationBar.openExtensions();
playExpect(await extensions.extensionIsInstalled(extensionLabelCrc)).toBeTruthy();
const extensionCard = await extensions.getInstalledExtension(extensionLabelNameCrc, extensionLabelCrc);
await playExpect(extensionCard.status).toHaveText(activeExtensionStatus);
await extensionCard.disableExtension();
await playExpect(extensionCard.status).toHaveText(disabledExtensionStatus);
});

test('Extension can be re-enabled correctly', async ({ navigationBar }) => {
const extensions = await navigationBar.openExtensions();
playExpect(await extensions.extensionIsInstalled(extensionLabelCrc)).toBeTruthy();
const extensionCard = await extensions.getInstalledExtension(extensionLabelNameCrc, extensionLabelCrc);
await playExpect(extensionCard.status).toHaveText(disabledExtensionStatus);
await extensionCard.enableExtension();
await playExpect(extensionCard.status).toHaveText(activeExtensionStatus);
});
});

test('OpenShift Local extension can be removed', async ({ navigationBar }) => {
await removeExtension(navigationBar);
});
});

async function removeExtension(navBar: NavigationBar): Promise<void> {
const extensions = await navBar.openExtensions();
const extensionCard = await extensions.getInstalledExtension(extensionLabelNameCrc, extensionLabelCrc);
await extensionCard.disableExtension();
await extensionCard.removeExtension();
await playExpect.poll(async () => await extensions.extensionIsInstalled(extensionLabelCrc), { timeout: 15000 }).toBeFalsy();
}
16 changes: 16 additions & 0 deletions tests/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"compilerOptions": {
"strictNullChecks": true,
"lib": [ "ES2017", "webworker" ],
"module": "esnext",
"target": "esnext",
"sourceMap": true,
"rootDir": "src",
"outDir": "dist",
"skipLibCheck": true,
"types": [ "node" ],
"allowSyntheticDefaultImports": true,
"moduleResolution": "Node",
"esModuleInterop": true
}
}
5 changes: 5 additions & 0 deletions vitest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ import path from 'node:path';
const config = {
test: {
include: ['**/*.{test,spec}.?(c|m)[jt]s?(x)'],
exclude: ['tests/**', '**/builtin/**',
'**/node_modules/**',
'**/dist/**',
'**/.{idea,git,cache,output,temp,cdix}/**',
'**/{.electron-builder,babel,changelog,docusaurus,jest,postcss,prettier,rollup,svelte,tailwind,vite,vitest*,webpack}.config.*',],
coverage: {
provider: 'v8',
reporter: ['lcov', 'text'],
Expand Down
Loading

0 comments on commit e723f56

Please sign in to comment.