mirror of https://github.com/waku-org/js-waku.git
feat: add playwright CI testing (#1542)
* add tests-browser package * rename to browser * add playwright and experiment with karma * add lock * remove karma * remove readme * replace default app, rename * add and configure playwright * up package-lock * use @waku/create-app, add scripts to handle it * remove tsconfig * update playwright script * move dependency to root * set folder * up * try install step * add playwright dep * remove step * add es module utils * fix import issue * run on master * use image prop * use dotenv-flow, set .env.local * add log, use dotenv-flow * add env var to ci * add env vars to CI * return install of deps * return container & log build step * upgrade @waku/create-app * fix firefox in container problem
This commit is contained in:
parent
a718c40882
commit
281d9b2c9d
|
@ -49,6 +49,7 @@
|
||||||
"**/*.spec.ts",
|
"**/*.spec.ts",
|
||||||
"**/tests/**",
|
"**/tests/**",
|
||||||
"**/rollup.config.js",
|
"**/rollup.config.js",
|
||||||
|
"**/playwright.config.ts",
|
||||||
"**/.eslintrc.cjs",
|
"**/.eslintrc.cjs",
|
||||||
"**/karma.conf.cjs"
|
"**/karma.conf.cjs"
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
name: Playwright tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
NODE_JS: "18"
|
||||||
|
EXAMPLE_TEMPLATE: "web-chat"
|
||||||
|
EXAMPLE_NAME: "example"
|
||||||
|
EXAMPLE_PORT: "8080"
|
||||||
|
# Firefox in container fails due to $HOME not being owned by user running commands
|
||||||
|
# more details https://github.com/microsoft/playwright/issues/6500
|
||||||
|
HOME: "/root"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
timeout-minutes: 60
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: mcr.microsoft.com/playwright:v1.38.0-jammy
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: ${{ env.NODE_JS }}
|
||||||
|
|
||||||
|
- uses: ./.github/actions/npm
|
||||||
|
|
||||||
|
- name: Run Playwright tests
|
||||||
|
run: npm run test --workspace=@waku/browser-tests
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: playwright-report
|
||||||
|
path: playwright-report/
|
||||||
|
retention-days: 30
|
|
@ -9,3 +9,6 @@ coverage
|
||||||
*.log
|
*.log
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
docs
|
docs
|
||||||
|
test-results
|
||||||
|
playwright-report
|
||||||
|
example
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -15,6 +15,7 @@
|
||||||
"packages/message-encryption",
|
"packages/message-encryption",
|
||||||
"packages/sdk",
|
"packages/sdk",
|
||||||
"packages/tests",
|
"packages/tests",
|
||||||
|
"packages/browser-tests",
|
||||||
"packages/build-utils"
|
"packages/build-utils"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
EXAMPLE_TEMPLATE="web-chat"
|
||||||
|
EXAMPLE_NAME="example"
|
||||||
|
EXAMPLE_PORT="8080"
|
|
@ -0,0 +1,14 @@
|
||||||
|
module.exports = {
|
||||||
|
parserOptions: {
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
project: "./tsconfig.dev.json"
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
node: true,
|
||||||
|
},
|
||||||
|
rules: {},
|
||||||
|
globals: {
|
||||||
|
process: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"name": "@waku/browser-tests",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"start": "run-s start:*",
|
||||||
|
"start:setup": "node ./src/setup-example.js",
|
||||||
|
"start:build": "node ./src/build-example.js",
|
||||||
|
"start:serve": "npx serve -p 8080 --no-port-switching ./example",
|
||||||
|
"test": "npx playwright test"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.37.1",
|
||||||
|
"@waku/create-app": "^0.1.1-7c24ffa",
|
||||||
|
"dotenv-flow": "^3.3.0",
|
||||||
|
"serve": "^14.2.1"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
import "dotenv-flow/config";
|
||||||
|
import { defineConfig, devices } from "@playwright/test";
|
||||||
|
|
||||||
|
const EXAMPLE_PORT = process.env.EXAMPLE_PORT;
|
||||||
|
// web-chat specific thingy
|
||||||
|
const EXAMPLE_TEMPLATE = process.env.EXAMPLE_TEMPLATE;
|
||||||
|
const BASE_URL = `http://127.0.0.1:${EXAMPLE_PORT}/${EXAMPLE_TEMPLATE}`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See https://playwright.dev/docs/test-configuration.
|
||||||
|
*/
|
||||||
|
export default defineConfig({
|
||||||
|
testDir: "./tests",
|
||||||
|
/* 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 ? 2 : undefined,
|
||||||
|
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||||
|
reporter: "html",
|
||||||
|
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||||
|
use: {
|
||||||
|
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||||
|
baseURL: BASE_URL,
|
||||||
|
|
||||||
|
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||||
|
trace: "on-first-retry"
|
||||||
|
},
|
||||||
|
|
||||||
|
/* Configure projects for major browsers */
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
name: "chromium",
|
||||||
|
use: { ...devices["Desktop Chrome"] }
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "firefox",
|
||||||
|
use: { ...devices["Desktop Firefox"] }
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "webkit",
|
||||||
|
use: { ...devices["Desktop Safari"] }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test against mobile viewports. */
|
||||||
|
// {
|
||||||
|
// name: 'Mobile Chrome',
|
||||||
|
// use: { ...devices['Pixel 5'] },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: 'Mobile Safari',
|
||||||
|
// use: { ...devices['iPhone 12'] },
|
||||||
|
// },
|
||||||
|
|
||||||
|
/* Test against branded browsers. */
|
||||||
|
// {
|
||||||
|
// name: 'Microsoft Edge',
|
||||||
|
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: 'Google Chrome',
|
||||||
|
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
|
||||||
|
// },
|
||||||
|
],
|
||||||
|
|
||||||
|
/* Run your local dev server before starting the tests */
|
||||||
|
webServer: {
|
||||||
|
url: BASE_URL,
|
||||||
|
stdout: "pipe",
|
||||||
|
stderr: "pipe",
|
||||||
|
command: "npm start",
|
||||||
|
reuseExistingServer: !process.env.CI,
|
||||||
|
timeout: 5 * 60 * 1000 // five minutes for bootstrapping an example
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,55 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
import "dotenv-flow/config";
|
||||||
|
import { execSync } from "child_process";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
import { __dirname } from "./utils.js";
|
||||||
|
|
||||||
|
const EXAMPLE_NAME = process.env.EXAMPLE_NAME;
|
||||||
|
const EXAMPLE_PATH = path.resolve(__dirname, "..", EXAMPLE_NAME);
|
||||||
|
|
||||||
|
const BUILD_FOLDER = "build";
|
||||||
|
const BUILD_PATH = path.resolve(EXAMPLE_PATH, BUILD_FOLDER);
|
||||||
|
|
||||||
|
// required by web-chat example
|
||||||
|
const WEB_CHAT_BUILD_PATH = path.resolve(EXAMPLE_PATH, "web-chat");
|
||||||
|
|
||||||
|
run();
|
||||||
|
|
||||||
|
function run() {
|
||||||
|
cleanPrevBuildIfExists();
|
||||||
|
buildExample();
|
||||||
|
renameBuildFolderForWebChat();
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanPrevBuildIfExists() {
|
||||||
|
try {
|
||||||
|
console.log("Cleaning previous build if exists.");
|
||||||
|
execSync(`rm -rf ${BUILD_PATH}`, { stdio: "ignore" });
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to clean previous build: ${error.message}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildExample() {
|
||||||
|
try {
|
||||||
|
console.log("Building example at", EXAMPLE_PATH);
|
||||||
|
execSync(`cd ${EXAMPLE_PATH} && npm run build`, { stdio: "pipe" });
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to build example: ${error.message}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renameBuildFolderForWebChat() {
|
||||||
|
try {
|
||||||
|
console.log("Renaming example's build folder.");
|
||||||
|
execSync(`mv ${BUILD_PATH} ${WEB_CHAT_BUILD_PATH}`, { stdio: "ignore" });
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
`Failed to rename build folder for web-chat: ${error.message}`
|
||||||
|
);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
import "dotenv-flow/config";
|
||||||
|
import { execSync } from "child_process";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
import { __dirname, readJSON } from "./utils.js";
|
||||||
|
|
||||||
|
const ROOT_PATH = path.resolve(__dirname, "../../../");
|
||||||
|
const JS_WAKU_PACKAGES = readWorkspaces();
|
||||||
|
|
||||||
|
const EXAMPLE_NAME = process.env.EXAMPLE_NAME;
|
||||||
|
const EXAMPLE_TEMPLATE = process.env.EXAMPLE_TEMPLATE;
|
||||||
|
const EXAMPLE_PATH = path.resolve(__dirname, "..", EXAMPLE_NAME);
|
||||||
|
|
||||||
|
run();
|
||||||
|
|
||||||
|
function run() {
|
||||||
|
cleanExampleIfExists();
|
||||||
|
bootstrapExample();
|
||||||
|
linkPackages();
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanExampleIfExists() {
|
||||||
|
try {
|
||||||
|
console.log("Cleaning previous example if exists.");
|
||||||
|
execSync(`rm -rf ${EXAMPLE_PATH}`, { stdio: "ignore" });
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to clean previous example: ${error.message}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function bootstrapExample() {
|
||||||
|
try {
|
||||||
|
console.log("Bootstrapping example.");
|
||||||
|
execSync(
|
||||||
|
`npx @waku/create-app --template ${EXAMPLE_TEMPLATE} ${EXAMPLE_NAME}`,
|
||||||
|
{ stdio: "ignore" }
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to bootstrap example: ${error.message}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function linkPackages() {
|
||||||
|
const examplePackage = readJSON(`${EXAMPLE_PATH}/package.json`);
|
||||||
|
|
||||||
|
// remove duplicates if any
|
||||||
|
const dependencies = filterWakuDependencies({
|
||||||
|
...examplePackage.dependencies,
|
||||||
|
...examplePackage.devDependencies
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(dependencies).forEach(linkDependency);
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterWakuDependencies(dependencies) {
|
||||||
|
return Object.entries(dependencies)
|
||||||
|
.filter((pair) => JS_WAKU_PACKAGES.includes(pair[0]))
|
||||||
|
.reduce((acc, pair) => {
|
||||||
|
acc[pair[0]] = pair[1];
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
function linkDependency(dependency) {
|
||||||
|
try {
|
||||||
|
console.log(`Linking dependency to example: ${dependency}`);
|
||||||
|
const pathToDependency = path.resolve(ROOT_PATH, toFolderName(dependency));
|
||||||
|
execSync(`npm link ${pathToDependency}`, { stdio: "ignore" });
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
`Failed to npm link dependency ${dependency} in example: ${error.message}`
|
||||||
|
);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function readWorkspaces() {
|
||||||
|
const rootPath = path.resolve(ROOT_PATH, "package.json");
|
||||||
|
const workspaces = readJSON(rootPath).workspaces;
|
||||||
|
return workspaces.map(toPackageName);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toPackageName(str) {
|
||||||
|
// assumption is that package name published is always the same in `@waku/package` name
|
||||||
|
return str.replace("packages", "@waku");
|
||||||
|
}
|
||||||
|
|
||||||
|
function toFolderName(str) {
|
||||||
|
return str.replace("@waku", "packages");
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { readFileSync } from "fs";
|
||||||
|
import { dirname } from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
export const __dirname = dirname(__filename);
|
||||||
|
|
||||||
|
export const readJSON = (path) => JSON.parse(readFileSync(path, "utf-8"));
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { expect, test } from "@playwright/test";
|
||||||
|
|
||||||
|
test("has title Web Chat title", async ({ page }) => {
|
||||||
|
await page.goto("");
|
||||||
|
await expect(page).toHaveTitle("Waku v2 chat app");
|
||||||
|
});
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.dev"
|
||||||
|
}
|
Loading…
Reference in New Issue