Skip to content

Commit

Permalink
chore(tests) add test coverage
Browse files Browse the repository at this point in the history
Signed-off-by: David Edler <david.edler@canonical.com>
  • Loading branch information
edlerd committed Feb 20, 2024
1 parent 625aa55 commit bf7f914
Show file tree
Hide file tree
Showing 20 changed files with 1,806 additions and 903 deletions.
133 changes: 133 additions & 0 deletions .github/workflows/coverage.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
name: Test coverage
on: pull_request

jobs:
e2e-coverage:
name: e2e-test-coverage
runs-on: ubuntu-latest
outputs:
job_status: ${{job.status}}
steps:
- uses: actions/checkout@v4

- name: Install Dotrun
run: |
sudo pip3 install dotrun
- name: Restore cached keys
uses: actions/cache/restore@v3
with:
path: keys
key: keys-folder

- name: Install LXD-UI dependencies
run: |
set -x
sudo chmod 0777 ../lxd-ui
dotrun install
- name: Run LXD-UI
env:
ENVIRONMENT: devel
PORT: 8407
LXD_UI_BACKEND_IP: 172.17.0.1
run: |
dotrun &
curl --head --fail --retry-delay 2 --retry 100 --retry-connrefused --insecure https://localhost:8407
- name: Set keys permissions
run: |
set -x
sudo chmod -R 0666 keys
sudo chmod 0777 keys
- name: Save keys
uses: actions/cache/save@v3
with:
path: keys
key: keys-folder

- name: Install LXD
uses: canonical/setup-lxd@v0.1.1
with:
channel: "latest/edge"

- name: Setup LXD
shell: bash
run: |
set -x
sudo lxc config set core.https_address "[::]:8443"
sudo lxc config trust add keys/lxd-ui.crt
sudo lxc config set cluster.https_address "127.0.0.1"
sudo lxc cluster enable local
- uses: actions/setup-node@v4
with:
node-version: 18

- name: Install Playwright Browser
run: npx playwright install --with-deps chromium

- name: Run tests with coverage
run: yarn test-e2e-coverage

- name: Upload coverage report
if: always()
uses: actions/upload-artifact@v4
with:
name: lxd-coverage
path: coverage/playwright-report
retention-days: 1

publish-coverage-report:
name: publish-coverage-report
runs-on: ubuntu-latest
needs: e2e-coverage
continue-on-error: true
steps:
- uses: actions/checkout@v4
with:
ref: gh-pages
token: ${{ secrets.GITHUB_TOKEN }}
- name: Download coverage report artifact
uses: actions/download-artifact@master
with:
name: lxd-coverage
- name: Unzip artifact and cleanup
run: |
ls -lah
find /
rm -rf coverage
mkdir coverage
unzip lxd-coverage.zip -d coverage
rm -rf lxd-coverage.zip
# user git configs are needed for git commands to work
# actual authentication is done using secrets.GITHUB_TOKEN with write permission
- name: Set Git User
run: |
git config --global user.email "github-action@example.com"
git config --global user.name "GitHub Action"
- name: Push HTML Report
timeout-minutes: 3
run: |
git add .
git commit -m "workflow: update coverage report"
# In case of another action job pushing to gh-pages while we are rebasing for the current job
while true; do
git pull --rebase
if [ $? -ne 0 ]; then
echo "Failed to rebase. Please review manually."
exit 1
fi
git push
if [ $? -eq 0 ]; then
echo "Successfully pushed HTML report to repo."
exit 0
fi
done
- name: Output Report URL as Worfklow Annotation
run: |
FULL_HTML_REPORT_URL=https://canonical.github.io/lxd-ui/coverage
echo "::notice title=Published Playwright Test Report::$FULL_HTML_REPORT_URL"
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,6 @@ dist/
/test-results/
/playwright-report/
/playwright/.cache/
/coverage

haproxy-local.cfg
haproxy-local.cfg
15 changes: 13 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"license": "LGPL-3.0-only",
"type": "module",
"scripts": {
"clean": "rm -rf node_modules yarn-error.log *.log build/ .jekyll-metadata .bundle playwright-report test-results haproxy-local.cfg",
"clean": "rm -rf node_modules yarn-error.log *.log build/ .jekyll-metadata .bundle playwright-report test-results haproxy-local.cfg coverage",
"build-html": "cp build/ui/index.html build/index.html",
"build": "npx vite build && yarn build-html",
"format-js-eslint": "eslint 'src/**/*.{json,jsx,tsx,ts}' 'tests/**/*.ts' --fix",
Expand All @@ -21,7 +21,11 @@
"serve": "./entrypoint",
"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"
"test-e2e-stable": "npx playwright test --project chromium:lxd-5.0-stable firefox:lxd-5.0-stable",
"test-js-coverage": "vitest --run --coverage",
"test-e2e-coverage": "yarn test-e2e-coverage-pw ; yarn test-e2e-coverage-report",
"test-e2e-coverage-pw": "rm -rf coverage/playwright* ; PW_TEST_HTML_REPORT_OPEN='never' npx playwright test profiles --project coverage",
"test-e2e-coverage-report": "nyc report --reporter html --reporter cobertura --reporter text --reporter text-summary --temp-dir coverage/playwright --report-dir coverage/playwright-report"
},
"dependencies": {
"@canonical/react-components": "0.50.2",
Expand Down Expand Up @@ -54,6 +58,7 @@
"@babel/preset-react": "7.23.3",
"@babel/preset-typescript": "7.23.3",
"@playwright/test": "1.41.0",
"@types/convert-source-map": "2.0.3",
"@types/cytoscape-popper": "2.0.4",
"@types/node-forge": "1.3.11",
"@types/react": "18.2.48",
Expand All @@ -64,15 +69,19 @@
"@typescript-eslint/eslint-plugin": "6.19.0",
"@typescript-eslint/parser": "6.19.0",
"@vitejs/plugin-react": "4.2.1",
"@vitest/coverage-istanbul": "1.2.2",
"autoprefixer": "10.4.16",
"babel-plugin-istanbul": "6.1.1",
"concurrently": "8.2.2",
"convert-source-map": "2.0.0",
"eslint": "8.56.0",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-prettier": "5.1.3",
"eslint-plugin-react": "7.33.2",
"husky": "8.0.3",
"lint-staged": "15.2.0",
"monaco-editor": "0.45.0",
"nyc": "^15.1.0",
"postcss": "8.4.33",
"postcss-cli": "11.0.0",
"prettier": "3.2.4",
Expand All @@ -83,7 +92,9 @@
"stylelint-prettier": "5.0.0",
"stylelint-scss": "6.0.0",
"typescript": "5.3.3",
"v8-to-istanbul": "9.2.0",
"vite": "4.5.2",
"vite-plugin-istanbul": "5.0.0",
"vite-tsconfig-paths": "4.3.1",
"vitest": "1.2.1"
},
Expand Down
16 changes: 12 additions & 4 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ import { TestOptions } from "./tests/fixtures/lxd-test";
const config: PlaywrightTestConfig<TestOptions> = {
testDir: "./tests",
/* Maximum time one test can run for. */
timeout: 120 * 1000,
timeout: 120_000,
expect: {
/**
* Maximum time expect() should wait for the condition to be met.
* For example in `await expect(locator).toHaveText();`
*/
timeout: 120 * 1000,
timeout: 120_000,
},
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : 3,
/* Opt out of parallel tests. */
workers: 1,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: process.env.CI ? "blob" : "html",
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
Expand Down Expand Up @@ -70,6 +70,14 @@ const config: PlaywrightTestConfig<TestOptions> = {
lxdVersion: "latest-edge",
},
},
{
name: "coverage",
use: {
...devices["Desktop Chrome"],
lxdVersion: "latest-edge",
hasCoverage: true,
},
},
],
};

Expand Down
75 changes: 75 additions & 0 deletions tests/fixtures/coverage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Page } from "@playwright/test";
import fs from "fs";
import { fromSource, removeMapFileComments } from "convert-source-map";
import v8ToIstanbul from "v8-to-istanbul";
import * as crypto from "crypto";
import { CoverageMapData } from "istanbul-lib-coverage";

export const startCoverage = async (
page: Page,
hasCoverage: boolean,
): Promise<void> => {
if (!hasCoverage) {
return;
}
await page.coverage.startJSCoverage();
};

export const finishCoverage = async (
page: Page,
hasCoverage: boolean,
): Promise<void> => {
if (!hasCoverage) {
return;
}
const coverage = await page.coverage.stopJSCoverage();
for (const entry of coverage) {
if (entry.url.endsWith(".css")) {
continue;
}
if (entry.url.includes("@vite")) {
continue;
}
if (entry.url.includes("spice")) {
continue;
}
const fileMatcher = entry.url.match(/http(s)*:\/\/.*:8407\/(?<file>.*)/);
if (!fileMatcher?.groups) {
continue;
}
const path = fileMatcher.groups.file;
const source = removeMapFileComments(entry.source ?? "");
const sourceMap = fromSource(entry.source ?? "") as { sourcemap: string };

const converter = v8ToIstanbul(path, 0, {
source,
sourceMap,
});
await converter.load();
converter.applyCoverage(entry.functions);
const istanbulCoverage = converter.toIstanbul() as CoverageMapData & {
[key: string]: { _coverageSchema: string };
};

const generateUUID = (): string => {
return crypto.randomBytes(16).toString("hex");
};

// a unique name for this report
const uuid = generateUUID();

// _coverageSchema is mandatory for nyc to parse the report
Object.entries(istanbulCoverage).forEach(([key]) => {
istanbulCoverage[key]["_coverageSchema"] = uuid;
});

const outDir = "coverage/playwright";
if (!fs.existsSync(outDir)) {
fs.mkdirSync(outDir, { recursive: true });
}
fs.writeFileSync(
`${outDir}/playwright_coverage_${uuid}.json`,
JSON.stringify(istanbulCoverage),
);
}
};
13 changes: 13 additions & 0 deletions tests/fixtures/lxd-test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
import { test as base } from "@playwright/test";
import { finishCoverage, startCoverage } from "./coverage";

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

export const test = base.extend<TestOptions>({
lxdVersion: ["latest-edge", { option: true }],
hasCoverage: [false, { option: true }],
});

test.beforeEach(async ({ page, hasCoverage }) => {
await startCoverage(page, hasCoverage);
});

test.afterEach(async ({ page, hasCoverage }) => {
await finishCoverage(page, hasCoverage);
});

export const expect = test.expect;
1 change: 1 addition & 0 deletions tests/helpers/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export const deleteNetwork = async (page: Page, network: string) => {
};

export const visitNetwork = async (page: Page, network: string) => {
await page.goto("/ui/");
await page.getByTitle("Networks (default)").click();
await page.getByRole("link", { name: network }).first().click();
};
Expand Down
Loading

0 comments on commit bf7f914

Please sign in to comment.