Skip to content

Commit cb7a329

Browse files
amisskiicbr7
andauthored
refactor: refactor resource card pages (podman-desktop#8310)
* refactor: resource cards Signed-off-by: Anton Misskii <[email protected]> * Update tests/playwright/src/model/pages/resource-connection-card-page.ts Co-authored-by: Vladimir Lazar <[email protected]> Signed-off-by: Anton Misskii <[email protected]> * refactor: add locators Signed-off-by: Anton Misskii <[email protected]> --------- Signed-off-by: Anton Misskii <[email protected]> Co-authored-by: Vladimir Lazar <[email protected]>
1 parent 9c946fd commit cb7a329

12 files changed

+214
-96
lines changed

tests/playwright/src/index.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export * from './utility/platform';
2828
export * from './utility/wait';
2929

3030
// exports Podman Desktop Page Object Module
31+
export * from './model/core/operations';
3132
export * from './model/core/platforms';
3233
export * from './model/core/states';
3334
export * from './model/core/types';
@@ -60,8 +61,10 @@ export * from './model/pages/pods-details-page';
6061
export * from './model/pages/pods-page';
6162
export * from './model/pages/pull-image-page';
6263
export * from './model/pages/registries-page';
64+
export * from './model/pages/resource-card-page';
65+
export * from './model/pages/resource-cli-card-page';
66+
export * from './model/pages/resource-connection-card-page';
6367
export * from './model/pages/resources-page';
64-
export * from './model/pages/resources-podman-connections-page';
6568
export * from './model/pages/run-image-page';
6669
export * from './model/pages/settings-bar';
6770
export * from './model/pages/settings-page';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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+
export enum ResourceElementActions {
20+
Start = 'Start',
21+
Restart = 'Restart',
22+
Stop = 'Stop',
23+
Delete = 'Delete',
24+
}

tests/playwright/src/model/core/states.ts

+5
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,8 @@ export enum VolumeState {
4747
Used = 'USED',
4848
Unused = 'UNUSED',
4949
}
50+
51+
export enum ResourceElementState {
52+
Running = 'RUNNING',
53+
Off = 'OFF',
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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 Locator, type Page } from '@playwright/test';
20+
21+
import { BasePage } from './base-page';
22+
23+
export abstract class ResourceCardPage extends BasePage {
24+
readonly parent: Locator;
25+
readonly card: Locator;
26+
readonly providerSetup: Locator;
27+
readonly providerConnections: Locator;
28+
readonly markdownContent: Locator;
29+
readonly setupButton: Locator;
30+
31+
constructor(page: Page, resourceName: string) {
32+
super(page);
33+
this.parent = this.page.getByRole('region', { name: 'content' });
34+
this.card = this.parent.getByRole('region', { name: resourceName, exact: true });
35+
this.providerSetup = this.card.getByRole('region', { name: 'Provider Setup', exact: true });
36+
this.providerConnections = this.card.getByRole('region', { name: 'Provider Connections', exact: true });
37+
this.markdownContent = this.providerConnections.getByLabel('markdown-content');
38+
this.setupButton = this.providerSetup.getByRole('button', { name: 'Setup' });
39+
}
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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+
21+
import { ResourceCardPage } from './resource-card-page';
22+
23+
export class ResourceCliCardPage extends ResourceCardPage {
24+
constructor(page: Page, resourceName: string) {
25+
super(page, resourceName);
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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 { expect as playExpect, type Locator, type Page } from '@playwright/test';
20+
21+
import type { ResourceElementActions } from '../core/operations';
22+
import { ResourceCardPage } from './resource-card-page';
23+
24+
export class ResourceConnectionCardPage extends ResourceCardPage {
25+
readonly resourceElement: Locator;
26+
readonly resourceElementDetailsButton: Locator;
27+
readonly resourceElementConnectionStatus: Locator;
28+
readonly resourceElementConnectionActions: Locator;
29+
readonly createButton: Locator;
30+
31+
constructor(page: Page, resourceName: string, resourceElementVisibleName?: string) {
32+
super(page, resourceName);
33+
this.resourceElement = this.providerConnections.getByRole('region', { name: resourceElementVisibleName });
34+
this.resourceElementDetailsButton = this.resourceElement.getByRole('button', { name: 'details' });
35+
this.resourceElementConnectionStatus = this.resourceElement.getByLabel('Connection Status Label');
36+
this.resourceElementConnectionActions = this.resourceElement.getByRole('group', { name: 'Connection Actions' });
37+
this.createButton = this.providerSetup.getByRole('button', { name: 'Create' });
38+
}
39+
40+
public async performConnectionAction(operation: ResourceElementActions): Promise<void> {
41+
const button = this.resourceElementConnectionActions.getByRole('button', { name: operation, exact: true });
42+
await playExpect(button).toBeEnabled({ timeout: 10000 });
43+
await button.click();
44+
}
45+
}

tests/playwright/src/model/pages/resources-page.ts

+5-6
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,15 @@ import { SettingsPage } from './settings-page';
2323
export class ResourcesPage extends SettingsPage {
2424
readonly heading: Locator;
2525
readonly featuredProviderResources: Locator;
26-
readonly podmanResources: Locator;
27-
readonly composeResources: Locator;
28-
readonly kindResources: Locator;
2926

3027
constructor(page: Page) {
3128
super(page, 'Resources');
3229
this.heading = this.header.getByRole('heading', { name: 'Title' }).and(this.header.getByText('Resources'));
3330
this.featuredProviderResources = this.content.getByRole('region', { name: 'Featured Provider Resources' });
34-
this.podmanResources = this.featuredProviderResources.getByRole('region', { name: 'podman', exact: true });
35-
this.composeResources = this.featuredProviderResources.getByRole('region', { name: 'Compose', exact: true });
36-
this.kindResources = this.featuredProviderResources.getByRole('region', { name: 'kind', exact: true });
31+
}
32+
33+
public async resourceCardIsVisible(resourceLabel: string): Promise<boolean> {
34+
const resourceCard = this.content.getByRole('region', { name: resourceLabel });
35+
return (await resourceCard.count()) > 0;
3736
}
3837
}

tests/playwright/src/model/pages/resources-podman-connections-page.ts

-46
This file was deleted.

tests/playwright/src/specs/compose-onboarding-smoke.spec.ts

+17-12
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { ComposeLocalInstallPage } from '../model/pages/compose-onboarding/compo
2727
import { ComposeOnboardingPage } from '../model/pages/compose-onboarding/compose-onboarding-page';
2828
import { ComposeVersionPage } from '../model/pages/compose-onboarding/compose-version-page';
2929
import { ComposeWideInstallPage } from '../model/pages/compose-onboarding/compose-wide-install-page';
30+
import { ResourceCliCardPage } from '../model/pages/resource-cli-card-page';
3031
import { ResourcesPage } from '../model/pages/resources-page';
3132
import { SettingsBar } from '../model/pages/settings-bar';
3233
import { WelcomePage } from '../model/pages/welcome-page';
@@ -35,6 +36,8 @@ import { PodmanDesktopRunner } from '../runner/podman-desktop-runner';
3536
import type { RunnerTestContext } from '../testContext/runner-test-context';
3637
import { isCI, isLinux } from '../utility/platform';
3738

39+
const RESOURCE_NAME: string = 'Compose';
40+
3841
let pdRunner: PodmanDesktopRunner;
3942
let page: Page;
4043
let navBar: NavigationBar;
@@ -70,10 +73,10 @@ describe.skipIf(isCI && isLinux)('Compose onboarding workflow verification', asy
7073
const settingsBar = new SettingsBar(page);
7174
const resourcesPage = await settingsBar.openTabPage(ResourcesPage);
7275

73-
await playExpect(resourcesPage.composeResources).toBeVisible();
74-
await resourcesPage.composeResources.scrollIntoViewIfNeeded();
75-
76-
const setupButton = resourcesPage.composeResources.getByRole('button', { name: 'Setup Compose' });
76+
await playExpect.poll(async () => resourcesPage.resourceCardIsVisible(RESOURCE_NAME)).toBeTruthy();
77+
const composeResourceCard = new ResourceCliCardPage(page, RESOURCE_NAME);
78+
await composeResourceCard.card.scrollIntoViewIfNeeded();
79+
const setupButton = composeResourceCard.setupButton;
7780
await playExpect(
7881
setupButton,
7982
'Compose Setup button is not present, perhaps compose is already installed',
@@ -139,14 +142,15 @@ describe.skipIf(isCI && isLinux)('Compose onboarding workflow verification', asy
139142

140143
test.skipIf(composePartialInstallation)('Verify Compose was installed', async () => {
141144
await navBar.openSettings();
142-
const resourcesPage = new ResourcesPage(page);
143-
const composeBox = resourcesPage.featuredProviderResources.getByRole('region', { name: 'Compose' });
144-
const setupButton = composeBox.getByRole('button', { name: 'Setup Compose' });
145+
const settingsBar = new SettingsBar(page);
146+
const resourcesPage = await settingsBar.openTabPage(ResourcesPage);
147+
await playExpect.poll(async () => await resourcesPage.resourceCardIsVisible(RESOURCE_NAME)).toBeTruthy();
148+
const composeBox = new ResourceCliCardPage(page, RESOURCE_NAME);
149+
const setupButton = composeBox.setupButton;
145150
await playExpect(setupButton).toBeHidden();
146151

147-
const settingsBar = new SettingsBar(page);
148152
const cliToolsPage = await settingsBar.openTabPage(CLIToolsPage);
149-
const composeRow = cliToolsPage.toolsTable.getByLabel('Compose');
153+
const composeRow = cliToolsPage.toolsTable.getByLabel(RESOURCE_NAME);
150154
const composeVersionInfo = composeRow.getByLabel('cli-version');
151155
await playExpect(composeVersionInfo).toHaveText('docker-compose ' + composeVersion);
152156
});
@@ -157,9 +161,10 @@ async function openComposeOnboarding(page: Page): Promise<ComposeOnboardingPage>
157161
const settingsBar = new SettingsBar(page);
158162
const resourcesPage = await settingsBar.openTabPage(ResourcesPage);
159163
await playExpect(resourcesPage.heading).toBeVisible();
160-
await playExpect(resourcesPage.composeResources).toBeVisible();
161-
await resourcesPage.composeResources.scrollIntoViewIfNeeded();
162-
const setupButton = resourcesPage.composeResources.getByRole('button', { name: 'Setup Compose' });
164+
await playExpect.poll(async () => await resourcesPage.resourceCardIsVisible(RESOURCE_NAME)).toBeTruthy();
165+
const composeResourceCard = new ResourceCliCardPage(page, RESOURCE_NAME);
166+
await composeResourceCard.card.scrollIntoViewIfNeeded();
167+
const setupButton = composeResourceCard.setupButton;
163168
await playExpect(
164169
setupButton,
165170
'Compose Setup button is not present, perhaps compose is already installed',

tests/playwright/src/specs/kind.spec.ts

+16-7
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,24 @@ import { afterAll, beforeAll, beforeEach, describe, test } from 'vitest';
2222

2323
import { ExtensionsPage } from '../model/pages/extensions-page';
2424
import { ResourcesPage } from '../model/pages/resources-page';
25+
import { SettingsBar } from '../model/pages/settings-bar';
2526
import { WelcomePage } from '../model/pages/welcome-page';
2627
import { NavigationBar } from '../model/workbench/navigation';
2728
import { StatusBar } from '../model/workbench/status-bar';
2829
import { PodmanDesktopRunner } from '../runner/podman-desktop-runner';
2930
import type { RunnerTestContext } from '../testContext/runner-test-context';
3031
import { waitForPodmanMachineStartup } from '../utility/wait';
3132

33+
const RESOURCE_NAME: string = 'kind';
34+
const EXTENSION_LABEL: string = 'podman-desktop.kind';
35+
3236
let pdRunner: PodmanDesktopRunner;
3337
let page: Page;
3438
let navigationBar: NavigationBar;
3539
let resourcesPage: ResourcesPage;
3640
let statusBar: StatusBar;
37-
const extensionLabel: string = 'podman-desktop.kind';
41+
let settingsBar: SettingsBar;
42+
3843
const skipKindInstallation = process.env.SKIP_KIND_INSTALL ? process.env.SKIP_KIND_INSTALL : false;
3944

4045
beforeAll(async () => {
@@ -47,6 +52,7 @@ beforeAll(async () => {
4752
navigationBar = new NavigationBar(page);
4853
resourcesPage = new ResourcesPage(page);
4954
statusBar = new StatusBar(page);
55+
settingsBar = new SettingsBar(page);
5056
});
5157

5258
beforeEach<RunnerTestContext>(async ctx => {
@@ -60,28 +66,31 @@ afterAll(async () => {
6066
describe('Kind End-to-End Tests', async () => {
6167
test.skipIf(skipKindInstallation)('Install Kind CLI', async () => {
6268
await navigationBar.openSettings();
63-
await playExpect(resourcesPage.kindResources).not.toBeVisible();
69+
await settingsBar.openTabPage(ResourcesPage);
70+
await playExpect.poll(async () => await resourcesPage.resourceCardIsVisible(RESOURCE_NAME)).toBeFalsy();
6471
await statusBar.installKindCLI();
6572
await playExpect(statusBar.kindInstallationButton).not.toBeVisible();
6673
});
6774
test('Verify that Kind CLI is installed', async () => {
6875
await navigationBar.openSettings();
69-
await playExpect(resourcesPage.kindResources).toBeVisible();
76+
await settingsBar.openTabPage(ResourcesPage);
77+
await playExpect.poll(async () => await resourcesPage.resourceCardIsVisible(RESOURCE_NAME)).toBeTruthy();
7078
});
7179
test('Kind extension lifecycle', async () => {
7280
const extensionsPage = new ExtensionsPage(page);
7381
await navigationBar.openExtensions();
74-
const kindExtension = await extensionsPage.getInstalledExtension('Kind extension', extensionLabel);
82+
const kindExtension = await extensionsPage.getInstalledExtension('Kind extension', EXTENSION_LABEL);
7583
await playExpect
76-
.poll(async () => await extensionsPage.extensionIsInstalled(extensionLabel), { timeout: 10000 })
84+
.poll(async () => await extensionsPage.extensionIsInstalled(EXTENSION_LABEL), { timeout: 10000 })
7785
.toBeTruthy();
7886
await playExpect(kindExtension.status).toHaveText('ACTIVE');
7987
await kindExtension.disableExtension();
8088
await navigationBar.openSettings();
81-
await playExpect(resourcesPage.kindResources).not.toBeVisible();
89+
await settingsBar.openTabPage(ResourcesPage);
90+
await playExpect.poll(async () => await resourcesPage.resourceCardIsVisible(RESOURCE_NAME)).toBeFalsy();
8291
await navigationBar.openExtensions();
8392
await kindExtension.enableExtension();
8493
await navigationBar.openSettings();
85-
await playExpect(resourcesPage.kindResources).toBeVisible();
94+
await playExpect.poll(async () => await resourcesPage.resourceCardIsVisible(RESOURCE_NAME)).toBeTruthy();
8695
});
8796
});

0 commit comments

Comments
 (0)