diff --git a/.github/workflows/publish-image.yml b/.github/workflows/publish-image.yml index 4632b65..736906d 100644 --- a/.github/workflows/publish-image.yml +++ b/.github/workflows/publish-image.yml @@ -25,9 +25,6 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Check playwright versions match - run: ./test/versions-match.sh - - name: Test Docker Image run: | docker build . --file Dockerfile -t $IMAGE_NAME --cache-from ghcr.io/mt-ag/$IMAGE_NAME diff --git a/.gitignore b/.gitignore index ee60177..185fd76 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ -/test/*/lctReporter.js -/test/*/playwright.config.js /test/*/output +/test/*/results.xml +/test/*/*.png diff --git a/Dockerfile b/Dockerfile index 6c3bf6f..e8196ed 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,16 @@ -FROM mcr.microsoft.com/playwright:v1.50.0-noble +FROM mcr.microsoft.com/playwright:v1.50.1-noble LABEL org.opencontainers.image.source https://github.com/mt-ag/lct-playwright-image -RUN mkdir /app && \ - mkdir /app/workdir && \ - mkdir /app/volume - -COPY ./files /app/workdir - -RUN cd /app/workdir && \ - yarn install && \ - yarn playwright install && \ - chown -R pwuser /app && \ - chmod +x /app/workdir/entrypoint.sh +# Playwright Installation +RUN mkdir -p /app/workdir && \ + cd /app && \ + npm config set --global update-notifier false && \ + npm install -y @playwright/test && \ + npx -y playwright@1.50.1 install --with-deps && \ + chown pwuser:pwuser /app && \ + chmod -R 777 /app WORKDIR /app/workdir -CMD ["bash", "entrypoint.sh"] +ENTRYPOINT ["npx", "playwright", "test", "test.spec.js"] diff --git a/README.md b/README.md index 6a4b2c2..7fc06c9 100644 --- a/README.md +++ b/README.md @@ -1 +1,26 @@ -# lct-playwright-image +# LCT Playwright Image + +## What we need + +- Node installed +- A prepared directory `/app` in the container with playwright installation +- A directory `/app/workdir` (must be subdir of above) which is mounted to the host to serve + - configs, like `playwright.config.js` + - the actual test specifications in subfolder `tests` + - as output directory for all artifacts + +## Sample folder structure for mounted folder + +## Building the Image locally + +`docker build . --file Dockerfile -t lct-playwright-image:local` + +## Testing the container locally + +```sh +# Run on Top Level directory of repo +rm -rf ./test/local/results.xml ./test/local/example.png ./test/local/output +docker run -u pwuser --rm --ipc=host -v ./test/local:/app/workdir lct-playwright-image:local "--project=chromium" +# Verify if files exist +ls -la ./test/local +``` diff --git a/files/package.json b/files/package.json index 1b6eb67..d1a0df5 100644 --- a/files/package.json +++ b/files/package.json @@ -1,7 +1,7 @@ { "license": "MIT", "dependencies": { - "@playwright/test": "1.50.0" + "@playwright/test": "1.50.1" }, "scripts": {} } diff --git a/run_tests.sh b/run_tests.sh index 0b68189..4e02b99 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -1,14 +1,13 @@ IMAGE_NAME="lct-playwright-image:latest" -function copy_test_files() { - FOLDER=$1 +function prepare_test() { - rm -rf /test/$FOLDER/output/ || true - rm ./test/$FOLDER/lctReporter.js || true - rm ./test/$FOLDER/playwright.config.js || true + rm -rf ./test/live || true + mkdir -p ./test/live/tests - cp ./test/lctReporter.js ./test/$FOLDER - cp ./test/playwright.config.js ./test/$FOLDER + cp ./test/static/lctReporter.js ./test/live + cp ./test/static/playwright.config.js ./test/live + cp ./test/static/test.spec.js ./test/live/tests } function single_file_exists() { @@ -22,29 +21,34 @@ function single_file_exists() { fi } -function test_result_files_exist() { - FOLDER=$1 +function check_result_and_cleanup() { + single_file_exists "./test/live/results.xml" + rm ./test/live/results.xml || true + single_file_exists "./test/live/example.png" + rm ./test/live/example.png || true + rm -rf ./test/live/output || true +} - single_file_exists "./test/$FOLDER/output/results.xml" - single_file_exists "./test/$FOLDER/output/output.log" +function run_container() { + BROWSER=$1 + docker run -u pwuser --rm --ipc=host -v $(pwd)/test/live:/app/workdir $IMAGE_NAME "--project=$BROWSER" } echo "Launching Test Suite..." -echo "Giving access to all files..." -chmod -R 777 ./test/* +prepare_test echo "Starting chromium test..." -copy_test_files "chromium" -docker run --rm --ipc=host -v $(pwd)/test/chromium:/app/volume $IMAGE_NAME -test_result_files_exist "chromium" +run_container "chromium" +echo "Verifying chromium test results..." +check_result_and_cleanup echo "Starting firefox test..." -copy_test_files "firefox" -docker run --rm --ipc=host -v $(pwd)/test/firefox:/app/volume $IMAGE_NAME -test_result_files_exist "firefox" +run_container "firefox" +echo "Verifying firefox test results..." +check_result_and_cleanup echo "Starting webkit test..." -copy_test_files "webkit" -docker run --rm --ipc=host -v $(pwd)/test/webkit:/app/volume $IMAGE_NAME -test_result_files_exist "webkit" +run_container "webkit" +echo "Verifying webkit test results..." +check_result_and_cleanup diff --git a/test/chromium/run.sh b/test/chromium/run.sh deleted file mode 100755 index 8c63269..0000000 --- a/test/chromium/run.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -echo "" -echo "Test script:" -cat test.spec.js -echo "" -echo "" -yarn playwright test test.spec.js --project=chromium diff --git a/test/firefox/run.sh b/test/firefox/run.sh deleted file mode 100755 index 9085b22..0000000 --- a/test/firefox/run.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -echo "" -echo "Test script:" -cat test.spec.js -echo "" -echo "" -yarn playwright test test.spec.js --project=firefox diff --git a/test/lctReporter.js b/test/local/lctReporter.js similarity index 100% rename from test/lctReporter.js rename to test/local/lctReporter.js diff --git a/test/playwright.config.js b/test/local/playwright.config.js similarity index 98% rename from test/playwright.config.js rename to test/local/playwright.config.js index 75a4daf..27e7451 100755 --- a/test/playwright.config.js +++ b/test/local/playwright.config.js @@ -26,6 +26,8 @@ const config = { retries: 2, /* Opt out of parallel tests on CI. */ workers: 1, + testDir: "./tests", + outputDir: "./output", /* Reporter to use. See https://playwright.dev/docs/test-reporters */ reporter: [["./lctReporter.js"], ["junit", { outputFile: "results.xml" }]], /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ diff --git a/test/chromium/test.spec.js b/test/local/tests/test.spec.js similarity index 100% rename from test/chromium/test.spec.js rename to test/local/tests/test.spec.js diff --git a/test/static/lctReporter.js b/test/static/lctReporter.js new file mode 100755 index 0000000..f60e82f --- /dev/null +++ b/test/static/lctReporter.js @@ -0,0 +1,79 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable no-unused-vars */ +/* eslint-disable no-console */ +/* eslint-disable class-methods-use-this */ + +function getStatusEmoji(status) { + switch (status) { + case 'passed': + return '✅'; + case 'failed': + return '❌'; + case 'timedOut': + return '⏱️'; + case 'skipped': + return '⏭️'; + default: + return '❓'; + } +} + +/** + * @typedef {import('@playwright/test/reporter').Reporter} PwReporter + */ + +/** @implements {PwReporter} */ +class MyReporter { + onBegin(config, suite) { + console.log( + `\n=====================================\n Starting LCT Worksheet with ${ + suite.allTests().length + } cases\n=====================================\n` + ); + } + + onTestBegin(test) { + console.log(`> Case "${test.title}"`); + } + + onTestEnd(test, result) { + console.log( + `> Finished Case "${test.title}" (${ + result.duration + } ms | Status ${getStatusEmoji(result.status)} ("${result.status}"))\n\n` + ); + + if (result?.error?.message) { + console.log(`Case error log:\n${result.error.message}\n\n\n`); + } + } + + onEnd(result) { + console.log( + `Finished Worksheet execution | Status ${getStatusEmoji( + result.status + )} ("${result.status}")` + ); + } + + onStepEnd(test, result, step) { + let title = step.title; + + if (title.includes('Screenshot on failure')) { + title = title.replace( + 'Screenshot on failure', + '🚨 Screenshot on failure 🚨' + ); + } + + console.log( + ` ${!step.error ? '✅' : '❌'} ${title} (${step.duration} ms)` + ); + + if (step.error) { + console.log(` ${step.error.message}`); + } + } +} + +module.exports = MyReporter; diff --git a/test/static/playwright.config.js b/test/static/playwright.config.js new file mode 100755 index 0000000..27e7451 --- /dev/null +++ b/test/static/playwright.config.js @@ -0,0 +1,108 @@ +import { devices } from "@playwright/test"; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// require('dotenv').config(); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +const config = { + /* Maximum time one test can run for. */ + // globalTimeout: 120 * 1000, + timeout: 30 * 1000, + expect: { + /** + * Maximum time expect() should wait for the condition to be met. + * For example in `await expect(locator).toHaveText();` + */ + timeout: 5000, + }, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + // forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: 2, + /* Opt out of parallel tests on CI. */ + workers: 1, + testDir: "./tests", + outputDir: "./output", + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: [["./lctReporter.js"], ["junit", { outputFile: "results.xml" }]], + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ + actionTimeout: 0, + /* Base URL to use in actions like `await page.goto('/')`. */ + // baseURL: 'http://localhost:3000', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: "on-first-retry", + screenshot: "only-on-failure", + ignoreHTTPSErrors: false, + }, + + /* 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: { + // channel: 'msedge', + // }, + // }, + // { + // name: 'Google Chrome', + // use: { + // channel: 'chrome', + // }, + // }, + ], + + /* Folder for test artifacts such as screenshots, videos, traces, etc. */ + // outputDir: 'test-results/', + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // port: 3000, + // }, +}; + +export default config; diff --git a/test/firefox/test.spec.js b/test/static/test.spec.js similarity index 100% rename from test/firefox/test.spec.js rename to test/static/test.spec.js diff --git a/test/versions-match.sh b/test/versions-match.sh deleted file mode 100755 index 4808761..0000000 --- a/test/versions-match.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -# Path to your Dockerfile and package.json -DOCKERFILE_PATH="./Dockerfile" -PACKAGE_JSON_PATH="./files/package.json" - -# Extract version from Dockerfile -DOCKERFILE_VERSION=$(grep '^FROM' $DOCKERFILE_PATH | awk -F':' '{print $2}' | sed 's/-noble//' | sed 's/v//') - -# Extract version from package.json -PACKAGE_JSON_VERSION=$(awk -F'"' '/@playwright\/test/ {print $4}' $PACKAGE_JSON_PATH) - -# Compare versions -if [ "$DOCKERFILE_VERSION" = "$PACKAGE_JSON_VERSION" ]; then - echo "Versions match: $DOCKERFILE_VERSION" -else - echo "Versions do not match. Dockerfile version: $DOCKERFILE_VERSION, package.json version: $PACKAGE_JSON_VERSION" - # Exit with error - exit 1 -fi diff --git a/test/webkit/run.sh b/test/webkit/run.sh deleted file mode 100755 index 41498a6..0000000 --- a/test/webkit/run.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -echo "" -echo "Test script:" -cat test.spec.js -echo "" -echo "" -yarn playwright test test.spec.js --project=webkit diff --git a/test/webkit/test.spec.js b/test/webkit/test.spec.js deleted file mode 100755 index 43bbee1..0000000 --- a/test/webkit/test.spec.js +++ /dev/null @@ -1,13 +0,0 @@ -import { expect, test } from "@playwright/test"; - -test.describe("Go to example.com", () => { - test("should open example.com", async ({ page }) => { - await page.goto("https://example.com"); - await page.screenshot({ path: "example.png" }); - }); - - test("should have header", async ({ page }) => { - await page.goto("https://example.com"); - await expect(page.locator("h1")).toHaveText("Example Domain"); - }); -});