From 1db01c5cc647739a52bb2404d5c3d1a3f5be6b70 Mon Sep 17 00:00:00 2001 From: Jeremias Peier Date: Mon, 1 Jul 2024 11:05:54 +0200 Subject: [PATCH 1/5] build: generate meta.json for visual regression runs --- .../continuous-integration-secure.yml | 2 -- src/visual-regression-app/src/interfaces.ts | 6 ++++ src/visual-regression-app/vite.config.ts | 30 +++++++++++++++---- src/vite-env.d.ts | 4 +++ .../prepare-failed-screenshots-artifact.ts | 7 +++-- .../visual-regression-plugin-config.ts | 24 +++++++++++++++ 6 files changed, 62 insertions(+), 11 deletions(-) diff --git a/.github/workflows/continuous-integration-secure.yml b/.github/workflows/continuous-integration-secure.yml index 6a2aab05d6..583fbdd6f8 100644 --- a/.github/workflows/continuous-integration-secure.yml +++ b/.github/workflows/continuous-integration-secure.yml @@ -126,8 +126,6 @@ jobs: path: dist/screenshots/ run-id: ${{ github.event.workflow_run.id }} github-token: ${{ secrets.GH_ACTIONS_ARTIFACT_DOWNLOAD }} - - name: Remove .keep file - run: rm dist/screenshots/.keep - name: Build visual-regression-app run: yarn build:visual-regression-app diff --git a/src/visual-regression-app/src/interfaces.ts b/src/visual-regression-app/src/interfaces.ts index b2105a7ce9..364c2e555c 100644 --- a/src/visual-regression-app/src/interfaces.ts +++ b/src/visual-regression-app/src/interfaces.ts @@ -9,3 +9,9 @@ export interface ScreenshotFiles { } export type ScreenshotMap = Record>>; + +export interface Meta { + gitSha: string; + baselineGitSha?: string; + commitUrl: string; +} diff --git a/src/visual-regression-app/vite.config.ts b/src/visual-regression-app/vite.config.ts index 8036fd34bc..a7a40d5bc4 100644 --- a/src/visual-regression-app/vite.config.ts +++ b/src/visual-regression-app/vite.config.ts @@ -13,7 +13,7 @@ import { import { distDir } from '../../tools/vite/index.js'; import rootConfig from '../../vite.config.js'; -import type { ScreenshotFiles } from './src/interfaces.js'; +import type { ScreenshotFiles, Meta } from './src/interfaces.js'; const packageRoot = new URL('.', import.meta.url); const screenshotsDir = new URL(`./screenshots/`, distDir); @@ -56,8 +56,11 @@ const extractHierarchicalMap = ( function prepareScreenshots(): PluginOption { let viteConfig: ResolvedConfig; - const virtualModuleId = 'virtual:screenshots'; - const resolvedVirtualModuleId = '\0' + virtualModuleId; + const virtualModuleScreenshotsId = 'virtual:screenshots'; + const resolvedVirtualModuleScreenshotsId = '\0' + virtualModuleScreenshotsId; + + const virtualModuleMetaId = 'virtual:meta'; + const resolvedVirtualModuleMetaId = '\0' + virtualModuleMetaId; return { name: 'prepare screenshot', @@ -65,12 +68,15 @@ function prepareScreenshots(): PluginOption { viteConfig = config; }, resolveId(id) { - if (id === virtualModuleId) { - return resolvedVirtualModuleId; + if (id === virtualModuleScreenshotsId) { + return resolvedVirtualModuleScreenshotsId; + } + if (id === virtualModuleMetaId) { + return resolvedVirtualModuleMetaId; } }, load(id) { - if (id === resolvedVirtualModuleId) { + if (id === resolvedVirtualModuleScreenshotsId) { if (!existsSync(screenshotsDir)) { return `export const screenshotsRaw = []`; } @@ -205,6 +211,18 @@ function prepareScreenshots(): PluginOption { (_key, value) => (value instanceof Map ? Object.fromEntries(Array.from(value)) : value), )}`; } + + if (id === resolvedVirtualModuleMetaId) { + let meta: Pick; + + try { + meta = JSON.parse(readFileSync(new URL('./meta.json', screenshotsDir), 'utf8')); + } catch { + meta = { gitSha: process.env.GITHUB_SHA ?? 'local', baselineGitSha: 'N/A' }; + } + + return `export const meta = ${JSON.stringify({ ...meta, commitUrl: `https://github.com/sbb-design-systems/lyne-components/commit/${meta.gitSha}` } satisfies Meta)};`; + } }, configureServer(server) { server.middlewares.use((req, res, next) => { diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 49888528ee..3f310d7119 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -13,3 +13,7 @@ declare module '@custom-elements-manifest/analyzer/cli' { declare module 'virtual:screenshots' { export const screenshotsRaw: import('./visual-regression-app/src/interfaces').ScreenshotMap; } + +declare module 'virtual:meta' { + export const meta: import('./visual-regression-app/src/interfaces').Meta; +} diff --git a/tools/visual-regression-testing/prepare-failed-screenshots-artifact.ts b/tools/visual-regression-testing/prepare-failed-screenshots-artifact.ts index 0e4ae70fbd..8fb98e00fd 100644 --- a/tools/visual-regression-testing/prepare-failed-screenshots-artifact.ts +++ b/tools/visual-regression-testing/prepare-failed-screenshots-artifact.ts @@ -1,9 +1,9 @@ -import { cpSync, existsSync, mkdirSync, writeFileSync, type CopySyncOptions } from 'node:fs'; +import { type CopySyncOptions, cpSync, existsSync, mkdirSync } from 'node:fs'; import * as glob from 'glob'; // When visual regression tests have failed, we only want to pack the relevant screenshots -// into the artifact transfered to the secure workflow, as uploading and downloading the full +// into the artifact transferred to the secure workflow, as uploading and downloading the full // baseline would take far longer. // Due to this we copy the necessary screenshots to /dist/screenshots-artifact which will // be moved to /dist/screenshots in the secure workflow. @@ -12,7 +12,8 @@ const screenshotDir = new URL('../../dist/screenshots/', import.meta.url); const artifactDir = new URL('../../dist/screenshots-artifact/', import.meta.url); const copyOptions: CopySyncOptions = { force: true, recursive: true }; mkdirSync(artifactDir, { recursive: true }); -writeFileSync(new URL('./.keep', artifactDir), '', 'utf8'); + +cpSync(new URL('./meta.json', screenshotDir), new URL('./meta.json', artifactDir), copyOptions); const failedDirs = glob.sync('*/failed/', { cwd: screenshotDir }); for (const failedDir of failedDirs.map((d) => `./${d}`)) { diff --git a/tools/web-test-runner/visual-regression-plugin-config.ts b/tools/web-test-runner/visual-regression-plugin-config.ts index c87c404782..d8ab8e36de 100644 --- a/tools/web-test-runner/visual-regression-plugin-config.ts +++ b/tools/web-test-runner/visual-regression-plugin-config.ts @@ -5,6 +5,9 @@ import { dirname, extname } from 'path'; import type { visualRegressionPlugin } from '@web/test-runner-visual-regression/plugin'; +import type { Meta } from '../../src/visual-regression-app/src/interfaces.js'; + +const metaFileName = 'meta.json'; const branch = process.env.GITHUB_REF_NAME ?? process.env.BRANCH ?? @@ -13,6 +16,27 @@ const baselineUrl = process.env.GITHUB_ACTIONS ? 'http://localhost:8050/' : 'https://lyne-visual-regression-baseline.app.sbb.ch/'; +// Importing distDir doesn't work +const screenshotsDir = new URL(`screenshots/`, new URL('../../dist/', import.meta.url)); + +let meta: Partial = { + gitSha: process.env.GITHUB_SHA ?? 'local', +}; + +let baselineMeta; +try { + const response = await fetch(`${baselineUrl}screenshots/${metaFileName}`); + baselineMeta = JSON.parse(await response.text()) satisfies Meta; + meta = { ...meta, baselineGitSha: baselineMeta.gitSha ?? 'N/A' }; +} catch (e) { + meta = { ...meta, baselineGitSha: 'N/A' }; +} + +if (!existsSync(screenshotsDir)) { + mkdirSync(screenshotsDir, { recursive: true }); +} +writeFileSync(new URL(`./${metaFileName}`, screenshotsDir), JSON.stringify(meta), 'utf8'); + export const visualRegressionConfig = { baseDir: 'dist/screenshots', async getBaseline({ filePath, name }) { From 22fc659b1df0ee162741964d9d2b48d2dd14a434 Mon Sep 17 00:00:00 2001 From: Jeremias Peier Date: Mon, 1 Jul 2024 11:41:48 +0200 Subject: [PATCH 2/5] build: show commits --- .../src/components/overview/overview.scss | 6 +++ .../src/components/overview/overview.ts | 47 ++++++++++++++++--- src/visual-regression-app/src/interfaces.ts | 3 +- src/visual-regression-app/vite.config.ts | 8 +++- 4 files changed, 55 insertions(+), 9 deletions(-) diff --git a/src/visual-regression-app/src/components/overview/overview.scss b/src/visual-regression-app/src/components/overview/overview.scss index 95240e4819..5ba7ad6ac5 100644 --- a/src/visual-regression-app/src/components/overview/overview.scss +++ b/src/visual-regression-app/src/components/overview/overview.scss @@ -7,3 +7,9 @@ gap: 1rem; flex-direction: column; } + +.app-compare-link { + @include sbb.mq($from: medium) { + margin-inline-start: auto; + } +} diff --git a/src/visual-regression-app/src/components/overview/overview.ts b/src/visual-regression-app/src/components/overview/overview.ts index 051dadb13f..0daa5bfbf2 100644 --- a/src/visual-regression-app/src/components/overview/overview.ts +++ b/src/visual-regression-app/src/components/overview/overview.ts @@ -1,12 +1,15 @@ import { LitElement, html, type TemplateResult, type CSSResultGroup, nothing } from 'lit'; import { customElement } from 'lit/decorators.js'; +// eslint-disable-next-line import-x/no-unresolved +import { meta } from 'virtual:meta'; import { screenshots } from '../../screenshots.js'; import style from './overview.scss?lit&inline'; import '@sbb-esta/lyne-elements/accordion.js'; -import '@sbb-esta/lyne-elements/button/secondary-button-link.js'; +import '@sbb-esta/lyne-elements/action-group.js'; +import '@sbb-esta/lyne-elements/button/button-link.js'; import '@sbb-esta/lyne-elements/card.js'; import '@sbb-esta/lyne-elements/container.js'; import '@sbb-esta/lyne-elements/expansion-panel.js'; @@ -28,14 +31,44 @@ export class Overview extends LitElement { Lyne visual regression comparison${screenshots.baselineOnly ? ' baseline' : nothing}
+ ${screenshots.stats} - ${screenshots.stats} - - ${!screenshots.baselineOnly ? `Start comparing` : `Check baselines`} - + ${meta.baselineGitSha + ? html`Baseline Commit + ${meta.baselineGitSha === 'N/A' + ? meta.baselineGitSha + : `#${meta.baselineGitSha.substring(0, 7)}`}` + : nothing} + ${meta.gitSha + ? html`Compare Commit + ${meta.gitSha === 'local' + ? meta.gitSha + : `#${meta.gitSha.substring(0, 7)}`}` + : nothing} + + ${!screenshots.baselineOnly ? `Start comparing` : `Check baselines`} + + ${screenshots.components.map( diff --git a/src/visual-regression-app/src/interfaces.ts b/src/visual-regression-app/src/interfaces.ts index 364c2e555c..565ee9a06a 100644 --- a/src/visual-regression-app/src/interfaces.ts +++ b/src/visual-regression-app/src/interfaces.ts @@ -12,6 +12,7 @@ export type ScreenshotMap = Record Date: Mon, 1 Jul 2024 11:52:54 +0200 Subject: [PATCH 3/5] feat: cache bustering --- .../image-diff/fullscreen-diff/fullscreen-diff.ts | 8 +++++++- .../components/test-case/image-diff/image-diff.ts | 12 +++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/visual-regression-app/src/components/test-case/image-diff/fullscreen-diff/fullscreen-diff.ts b/src/visual-regression-app/src/components/test-case/image-diff/fullscreen-diff/fullscreen-diff.ts index 966a8d6e26..06a2d1152e 100644 --- a/src/visual-regression-app/src/components/test-case/image-diff/fullscreen-diff/fullscreen-diff.ts +++ b/src/visual-regression-app/src/components/test-case/image-diff/fullscreen-diff/fullscreen-diff.ts @@ -1,5 +1,7 @@ import { LitElement, html, type TemplateResult, type CSSResultGroup, nothing } from 'lit'; import { customElement, property } from 'lit/decorators.js'; +// eslint-disable-next-line import-x/no-unresolved +import { meta } from 'virtual:meta'; import type { ScreenshotFiles } from '../../../../interfaces.js'; @@ -47,7 +49,11 @@ export class FullscreenDiff extends LitElement { : nothing}
- +
`; } } diff --git a/src/visual-regression-app/src/components/test-case/image-diff/image-diff.ts b/src/visual-regression-app/src/components/test-case/image-diff/image-diff.ts index ae8d9b7389..1e3ea8c4c1 100644 --- a/src/visual-regression-app/src/components/test-case/image-diff/image-diff.ts +++ b/src/visual-regression-app/src/components/test-case/image-diff/image-diff.ts @@ -1,5 +1,7 @@ import { LitElement, html, type TemplateResult, type CSSResultGroup, nothing } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; +// eslint-disable-next-line import-x/no-unresolved +import { meta } from 'virtual:meta'; import type { ScreenshotFiles } from '../../../interfaces.js'; @@ -104,7 +106,7 @@ export class ImageDiff extends LitElement { > @@ -120,7 +122,11 @@ export class ImageDiff extends LitElement { class="app-image-button" ?hidden=${!this._showDiff || this.screenshotFiles.isNew} > - +