Skip to content

Commit

Permalink
chore(test): added tests for multiple lxd server versions
Browse files Browse the repository at this point in the history
Signed-off-by: Mason Hu <[email protected]>
  • Loading branch information
mas-who authored and edlerd committed Feb 20, 2024
1 parent fdbda74 commit d4abd51
Show file tree
Hide file tree
Showing 20 changed files with 179 additions and 44 deletions.
19 changes: 14 additions & 5 deletions .github/workflows/pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ jobs:
strategy:
fail-fast: false
matrix:
lxd_channel: ["5.0/stable", "latest/edge"]
browser: ["chromium", "firefox"]
outputs:
job_status: ${{job.status}}
Expand Down Expand Up @@ -114,7 +115,7 @@ jobs:
- name: Install LXD
uses: canonical/[email protected]
with:
channel: latest/edge
channel: ${{ matrix.lxd_channel }}

- name: Setup LXD
shell: bash
Expand All @@ -132,17 +133,25 @@ jobs:
- name: Install Playwright Browsers
run: npx playwright install --with-deps ${{ matrix.browser }}

- name: Set lxd channel env variable
id: lxd-env
run: |
# need to change / to - in lxd channel string for report naming
LXD_CHANNEL=$(echo '${{ matrix.lxd_channel }}' | sed 's#/#-#g')
echo "LXD_CHANNEL=$LXD_CHANNEL" >> $GITHUB_OUTPUT
- name: Run Playwright tests
run: npx playwright test --project ${{ matrix.browser }}
run: npx playwright test --project ${{ matrix.browser }}:lxd-${{ steps.lxd-env.outputs.LXD_CHANNEL }}

- name: Rename Playwright report
run: mv blob-report/report.zip blob-report/${{ matrix.browser }}-report.zip
if: always()
run: mv blob-report/report.zip blob-report/${{ matrix.browser }}-lxd-${{ steps.lxd-env.outputs.LXD_CHANNEL }}-report.zip

- name: Upload ${{ matrix.browser }} blob reports to be merged
- name: Upload ${{ matrix.browser }}-lxd-${{ steps.lxd-env.outputs.LXD_CHANNEL }} blob reports to be merged
if: always()
uses: actions/upload-artifact@v4
with:
name: blob-report-${{ matrix.browser }}
name: blob-report-${{ matrix.browser }}-lxd-${{ steps.lxd-env.outputs.LXD_CHANNEL }}
path: blob-report
retention-days: 1

Expand Down
6 changes: 6 additions & 0 deletions HACKING.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ All commits are required to be signed off using a GPG key. You can use the follo
3. To sign commits, you should enter the git command with additional flags as shown in this example: `git commit -s -S -a -m "initial commit"`.
4. To make your life a little easier, you can setup a git alias for signing commits with `git config alias.sc 'commit -s -S -a'`. Now you can sign your commits with `git sc -m "initial commit"` for example. Note this only enables the alias for your local git configuration.

# Supporting multiple lxd versions
When making a contribution to this project, please take note that the UI should degrade gracefully for all lxd LTS versions later than v5.0. To acheive this, there are two processes that should be followed:

1. When adding a new feature that introduces lxd api endpoints that are not currently used in the project, make sure you check against `api_extensions` returned by the `GET /1.0/` endpoint if the new endpoints used exists for older lxd versions. If the new endpoints are not supported in an older lxd version, then you should either hide or disable a portion of the new feature for the relevant lxd version. A useful `useSupportedFeatures` hook can be used for this purpose. You can also find a comprehensive list of `api_extensions` refrences in the [lxd documentation](https://documentation.ubuntu.com/lxd/en/latest/api-extensions/).
2. You should write e2e tests that covers the new feature for all supported lxd versions. For example, if your feature is hidden for an older lxd server version, you should test that it is not displayed in the browser for that lxd version.

# End-to-end tests

Install playwright and its browsers
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
"hooks-remove": "husky uninstall",
"start": "concurrently --kill-others --raw 'vite --host | grep -v 3000' 'yarn serve'",
"serve": "./entrypoint",
"test-js": "vitest --run"
"test-js": "vitest --run",
"test-e2e-edge": "npx playwright test --project chromium:lxd-latest-edge firefox:lxd-latest-edge",
"test-e2e-stable": "npx playwright test --project chromium:lxd-5.0-stable firefox:lxd-5.0-stable"
},
"dependencies": {
"@canonical/react-components": "0.50.2",
Expand Down
24 changes: 20 additions & 4 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import type { PlaywrightTestConfig } from "@playwright/test";
import { devices } from "@playwright/test";
import { TestOptions } from "./tests/fixtures/lxd-test";

/**
* See https://playwright.dev/docs/test-configuration.
*/
const config: PlaywrightTestConfig = {
const config: PlaywrightTestConfig<TestOptions> = {
testDir: "./tests",
/* Maximum time one test can run for. */
timeout: 120 * 1000,
Expand Down Expand Up @@ -42,16 +43,31 @@ const config: PlaywrightTestConfig = {
/* Configure projects for major browsers */
projects: [
{
name: "chromium",
name: "chromium:lxd-5.0-stable",
use: {
...devices["Desktop Chrome"],
lxdVersion: "5.0-stable",
},
},
{
name: "firefox:lxd-5.0-stable",
use: {
...devices["Desktop Firefox"],
lxdVersion: "5.0-stable",
},
},
{
name: "chromium:lxd-latest-edge",
use: {
...devices["Desktop Chrome"],
lxdVersion: "latest-edge",
},
},

{
name: "firefox",
name: "firefox:lxd-latest-edge",
use: {
...devices["Desktop Firefox"],
lxdVersion: "latest-edge",
},
},
],
Expand Down
3 changes: 2 additions & 1 deletion tests/cluster.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Page, test } from "@playwright/test";
import { Page } from "@playwright/test";
import { test } from "./fixtures/lxd-test";
import { randomNameSuffix } from "./helpers/name";

test("cluster group create and delete", async ({ page }) => {
Expand Down
2 changes: 1 addition & 1 deletion tests/devices.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { test } from "@playwright/test";
import { test } from "./fixtures/lxd-test";
import {
deleteProfile,
finishProfileCreation,
Expand Down
10 changes: 10 additions & 0 deletions tests/fixtures/lxd-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { test as base } from "@playwright/test";

export type LxdVersions = "5.0-stable" | "latest-edge";
export type TestOptions = {
lxdVersion: LxdVersions;
};

export const test = base.extend<TestOptions>({
lxdVersion: ["latest-edge", { option: true }],
});
19 changes: 13 additions & 6 deletions tests/helpers/configuration.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Page } from "@playwright/test";
import { LxdVersions } from "../fixtures/lxd-test";

export const setOption = async (page: Page, field: string, value: string) => {
await activateOverride(page, field);
Expand Down Expand Up @@ -89,14 +90,20 @@ export const setMemLimit = async (
await page.getByPlaceholder(text).fill(limit);
};

export const setSchedule = async (page: Page, value: string) => {
await activateOverride(
page,
"Schedule Schedule for automatic instance snapshots - From: LXD",
);
export const setSchedule = async (
page: Page,
value: string,
lxdVersion: LxdVersions,
) => {
const scheduleFieldText =
lxdVersion === "5.0-stable"
? "Schedule"
: "Schedule Schedule for automatic instance snapshots - From: LXD";

await activateOverride(page, scheduleFieldText);
await page
.getByRole("row", {
name: "Schedule Schedule for automatic instance snapshots - From: LXD",
name: scheduleFieldText,
})
.getByText("Cron syntax")
.click();
Expand Down
3 changes: 2 additions & 1 deletion tests/instance-panel.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Page, test } from "@playwright/test";
import { Page } from "@playwright/test";
import { test } from "./fixtures/lxd-test";
import {
createInstance,
deleteInstance,
Expand Down
7 changes: 4 additions & 3 deletions tests/instances.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Page, expect, test } from "@playwright/test";
import { Page, expect } from "@playwright/test";
import { test } from "./fixtures/lxd-test";
import {
createInstance,
deleteInstance,
Expand Down Expand Up @@ -157,7 +158,7 @@ test("instance edit security policies", async () => {
);
});

test("instance edit snapshot configuration", async () => {
test("instance edit snapshot configuration", async ({ lxdVersion }) => {
await editInstance(page, instance);

await page
Expand All @@ -167,7 +168,7 @@ test("instance edit snapshot configuration", async () => {
await setInput(page, "Snapshot name", "Enter name pattern", "snap123");
await setInput(page, "Expire after", "Enter expiry expression", "3m");
await setOption(page, "Snapshot stopped instances", "true");
await setSchedule(page, "@daily");
await setSchedule(page, "@daily", lxdVersion);

await saveInstance(page, instance);

Expand Down
53 changes: 48 additions & 5 deletions tests/iso-volumes.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { test } from "@playwright/test";
import { expect } from "@playwright/test";
import { test } from "./fixtures/lxd-test";
import { randomNameSuffix } from "./helpers/name";
import { deleteInstance, randomInstanceName } from "./helpers/instances";

Expand All @@ -8,7 +9,11 @@ export const randomIso = (): string => {
return `playwright-iso-${randomNameSuffix()}`;
};

test("upload and delete custom iso", async ({ page }) => {
test("upload and delete custom iso", async ({ page, lxdVersion }) => {
test.skip(
lxdVersion === "5.0-stable",
"custom storage volume iso import not supported in lxd v5.0/stable",
);
const isoName = randomIso();

await page.goto("/ui/");
Expand Down Expand Up @@ -39,8 +44,11 @@ test("upload and delete custom iso", async ({ page }) => {
await page.getByText(`Custom iso ${isoName} deleted.`).click();
});

test("use custom iso for instance launch", async ({ page }) => {
test.skip(Boolean(process.env.CI), "github runners lack vm support");
test("use custom iso for instance launch", async ({ page, lxdVersion }) => {
test.skip(
lxdVersion === "5.0-stable",
"custom storage volume iso import not supported in lxd v5.0/stable",
);

const instance = randomInstanceName();
const isoName = randomIso();
Expand All @@ -57,7 +65,9 @@ test("use custom iso for instance launch", async ({ page }) => {
.locator("#name")
.fill(isoName);
await page.getByRole("button", { name: "Upload", exact: true }).click();
await page.locator(".u-align--right > .p-button--positive").click();
await page
.locator(".u-align--right > .p-button--positive", { hasText: "Select" })
.click();
await page.getByRole("button", { name: "Create" }).click();

await page.waitForSelector(`text=Created instance ${instance}.`);
Expand All @@ -71,3 +81,36 @@ test("use custom iso for instance launch", async ({ page }) => {
await page.getByText("Delete", { exact: true }).click();
await page.getByText(`Custom iso ${isoName} deleted.`).click();
});

test("not allowed to upload custom iso for lxd v5.0/stable", async ({
page,
lxdVersion,
}) => {
test.skip(
lxdVersion !== "5.0-stable",
`this test is specific to lxd v5.0/stable, current lxd snap channel is ${lxdVersion}`,
);
await page.goto("/ui/");
await page.getByRole("link", { name: "Storage", exact: true }).click();
await expect(page.getByTestId("tab-link-Custom ISOs")).toBeHidden();
});

test("not allowed to launch instance with custom iso for lxd v5.0/stable", async ({
page,
lxdVersion,
}) => {
test.skip(Boolean(process.env.CI), "github runners lack vm support");
test.skip(
lxdVersion !== "5.0-stable",
`this test is specific to lxd v5.0/stable, current lxd snap channel is ${lxdVersion}`,
);

const instance = randomInstanceName();
await page.goto("/ui/");
await page.getByRole("link", { name: "Instances", exact: true }).click();
await page.getByRole("button", { name: "Create instance" }).click();
await page.getByLabel("Instance name").fill(instance);
await expect(
page.getByRole("button", { name: "Use custom ISO" }),
).toBeHidden();
});
3 changes: 2 additions & 1 deletion tests/networks.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Page, test } from "@playwright/test";
import { Page } from "@playwright/test";
import { test } from "./fixtures/lxd-test";
import {
createNetwork,
createNetworkForward,
Expand Down
3 changes: 2 additions & 1 deletion tests/notification.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { test, expect, Page } from "@playwright/test";
import { expect, Page } from "@playwright/test";
import { test } from "./fixtures/lxd-test";
import {
createAndStartInstance,
createInstance,
Expand Down
2 changes: 1 addition & 1 deletion tests/profile-summary-panel.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { test } from "@playwright/test";
import { test } from "./fixtures/lxd-test";
import {
createProfile,
deleteProfile,
Expand Down
7 changes: 4 additions & 3 deletions tests/profiles.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Page, test } from "@playwright/test";
import { Page } from "@playwright/test";
import { test } from "./fixtures/lxd-test";
import {
assertCode,
assertReadMode,
Expand Down Expand Up @@ -120,14 +121,14 @@ test("profile security policies", async () => {
await assertReadMode(page, "Enable secureboot (VMs only)", "true");
});

test("profile snapshots", async () => {
test("profile snapshots", async ({ lxdVersion }) => {
await editProfile(page, profile);
await page.getByText("Snapshots").click();

await setInput(page, "Snapshot name", "Enter name pattern", "snap123");
await setInput(page, "Expire after", "Enter expiry expression", "3m");
await setOption(page, "Snapshot stopped instances", "true");
await setSchedule(page, "@daily");
await setSchedule(page, "@daily", lxdVersion);

await saveProfile(page, profile);

Expand Down
19 changes: 15 additions & 4 deletions tests/projects.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { expect, test } from "@playwright/test";
import { expect } from "@playwright/test";
import { test } from "./fixtures/lxd-test";
import {
assertReadMode,
setInput,
Expand Down Expand Up @@ -28,7 +29,7 @@ test("project rename", async ({ page }) => {
await deleteProject(page, newName);
});

test("project edit configuration", async ({ page }) => {
test("project edit configuration", async ({ page, lxdVersion }) => {
const project = randomProjectName();
await createProject(page, project);

Expand All @@ -39,7 +40,13 @@ test("project edit configuration", async ({ page }) => {
.getByRole("combobox", { name: "Features" })
.selectOption("customised");
await page.locator("span").filter({ hasText: "Networks" }).click();
await page.locator("label").filter({ hasText: "Network zones" }).click();
if (lxdVersion === "5.0-stable") {
await expect(
page.locator("label").filter({ hasText: "Network zones" }),
).toBeHidden();
} else {
await page.locator("label").filter({ hasText: "Network zones" }).click();
}
await page.getByText("Allow custom restrictions on a project level").click();

await page.getByText("Resource limits").click();
Expand Down Expand Up @@ -102,7 +109,11 @@ test("project edit configuration", async ({ page }) => {

await page.getByText("DescriptionA-new-description").click();
await expect(page.locator("input#features_networks")).toHaveValue("on");
await expect(page.locator("input#features_networks_zones")).toHaveValue("on");
if (lxdVersion !== "5.0-stable") {
await expect(page.locator("input#features_networks_zones")).toHaveValue(
"on",
);
}

await page.getByText("Resource limits").click();
await assertReadMode(page, "Max number of instances", "1");
Expand Down
Loading

0 comments on commit d4abd51

Please sign in to comment.