Skip to content

Commit

Permalink
feat: basic reporting
Browse files Browse the repository at this point in the history
  • Loading branch information
dlockhart committed Jul 6, 2023
2 parents bd308db + cc995e7 commit ff072d0
Show file tree
Hide file tree
Showing 9 changed files with 703 additions and 15 deletions.
255 changes: 247 additions & 8 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"repository": "https://github.com/BrightspaceUI/testing.git",
"scripts": {
"lint": "eslint . --ext .js",
"start": "web-dev-server --root-dir ./.vdiff --open ./report/",
"test": "npm run lint && npm run test:server && npm run test:browser",
"test:browser": "web-test-runner --files \"./test/browser/**/*.test.js\" --node-resolve --playwright",
"test:server": "mocha ./test/server/**/*.test.js",
Expand All @@ -15,11 +16,17 @@
"author": "D2L Corporation",
"license": "Apache-2.0",
"devDependencies": {
"@rollup/plugin-node-resolve": "^15",
"@web/dev-server": "^0.2",
"@web/rollup-plugin-html": "^2",
"chai": "^4",
"deepmerge": "^4",
"eslint": "^8",
"eslint-config-brightspace": "^0.23",
"lit": "^2",
"mocha": "^10",
"page": "^1",
"rollup": "^3",
"sinon": "^15"
},
"exports": {
Expand Down
133 changes: 133 additions & 0 deletions src/server/report/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import './test-result.js';
import { css, html, LitElement } from 'lit';
import data from './data.js';
import page from 'page';

class App extends LitElement {
static properties = {
_filterFile: { state: true },
_filterTest: { state: true },
_mode: { state: true },
_overlay: { state: true }
};
static styles = [css`
table {
border-collapse: collapse;
}
td, th {
border: 1px solid #cdd5dc;
padding: 10px;
}
thead th {
background-color: #f9fbff;
}
tbody th {
font-weight: normal;
text-align: left;
}
`];
constructor() {
super();
this._mode = 'sideBySide';
this._overlay = true;
}
connectedCallback() {
super.connectedCallback();
this._root = new URL(window.location.href).pathname;
page(this._root, (ctx) => {
const searchParams = new URLSearchParams(ctx.querystring);
if (searchParams.has('file')) {
this._filterFile = searchParams.get('file');
if (searchParams.has('test')) {
this._filterTest = searchParams.get('test');
} else {
this._filterTest = undefined;
}
} else {
this._filterFile = undefined;
this._filterTest = undefined;
}
});
page();
}
render() {
let view;
if (this._filterFile !== undefined && this._filterTest !== undefined) {
const fileData = data.files.find(f => f.name === this._filterFile);
if (!fileData) {
view = html`<p>File not found: <b>${this._filterFile}</b>.</p>`;
} else {
const testData = fileData.tests.find(t => t.name === this._filterTest);
if (!testData) {
view = html`<p>Test not found: <b>${this._filterTest}</b>.</p>`;
} else {
view = this._renderTest(fileData, testData);
}
}
} else {
view = data.files.map(f => this._renderFile(f));
}
return html`
<header>
<h1>Visual-diff Results</h1>
</header>
<main>${view}</main>
`;
}
_goHome() {
page(this._root);
}
_handleModeChange(e) {
this._mode = e.target.options[e.target.selectedIndex].value;
}
_handleOverlayChange(e) {
this._overlay = e.target.checked;
}
_renderFile(file) {
return html`
<h2>${file.name}</h2>
<table>
<thead>
<tr>
<th>Test</th>
${data.browsers.map(b => html`<th>${b.name}</th>`)}
</tr>
</thead>
<tbody>
${file.tests.map(t => this._renderTestResultRow(file, t))}
</tbody>
</table>
`;
}
_renderTest(file, test) {
return html`
<h2>${test.name} (${(test.results.length - test.numFailed)}/${test.results.length} passed)</h2>
<div>
<label>Mode:
<select @change="${this._handleModeChange}">
<option value="sideBySide" ?selected="${this._mode === 'sideBySide'}">Side-by-side</option>
<option value="oneUpOriginal" ?selected="${this._mode === 'oneUpOriginal'}">One-up (original)</option>
<option value="oneUpNew" ?selected="${this._mode === 'oneUpNew'}">One-up (new)</option>
</select>
</label>
<label><input type="checkbox" ?checked="${this._overlay}" @change="${this._handleOverlayChange}">Show overlay</label>
</div>
${test.results.map(r => html`<d2l-vdiff-report-test-result browser="${r.name}" mode="${this._mode}" file="${file.name}" ?show-overlay="${this._overlay}" test="${test.name}"></d2l-vdiff-report-test-result>`)}
<div style="margin-top: 20px;">
<button @click="${this._goHome}">Back</button>
</div>
`;
}
_renderTestResultRow(file, test) {
const results = test.results.map(r => {
return html`<td>${r.passed.toString()}</td>`;
});
return html`
<tr>
<th><a href="./?file=${encodeURIComponent(file.name)}&test=${encodeURIComponent(test.name)}">${test.name}</a></th>
${results}
</tr>
`;
}
}
customElements.define('d2l-vdiff-report-app', App);
21 changes: 21 additions & 0 deletions src/server/report/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Visual Diff Report</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<style>
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap');
body {
font-family: 'Roboto', sans-serif;
font-size: 16px;
}
</style>
</head>
<body>
<d2l-vdiff-report-app></d2l-vdiff-report-app>
<script type="module" src="app.js"></script>
</body>
</html>
110 changes: 110 additions & 0 deletions src/server/report/test-result.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { css, html, LitElement, nothing } from 'lit';
import data from './data.js';

class TestResult extends LitElement {
static properties = {
browser: { type: String },
mode: { type: String },
file: { type: String },
showOverlay: { attribute: 'show-overlay', type: Boolean },
test: { type: String },
};
static styles = [css`
.side-by-side {
flex-direction: row;
flex-wrap: nowrap;
display: flex;
gap: 10px;
}
.side-by-side > div {
flex: 0 1 50%;
}
img {
max-width: 100%;
}
.diff-container {
background: repeating-conic-gradient(#cccccc 0% 25%, #ffffff 0% 50%) 50% / 20px 20px;
background-position: 0 0;
border: 1px solid #cccccc;
display: inline-block;
line-height: 0;
}
.overlay-container {
position: relative;
}
.overlay {
background: hsla(0,0%,100%,.8);
position: absolute;
top: 0;
left: 0;
}
.no-changes {
border: 1px solid #cccccc;
padding: 20px;
}
`];
render() {

const { browserData, fileData, resultData, testData } = this._fetchData();
if (!browserData || !fileData || !resultData || !testData) return nothing;

return html`
<h3>${resultData.name} v${browserData.version} (${resultData.duration}ms)</h3>
${this._renderBody(resultData)}
`;

}
_fetchData() {

const fileData = data.files.find(f => f.name === this.file);
if (!fileData) return {};

const testData = fileData.tests.find(t => t.name === this.test);
if (!testData) return {};

const resultData = testData.results.find(r => r.name === this.browser);
if (!resultData) return {};

const browserData = data.browsers.find(b => b.name === this.browser);
if (!browserData) return {};

return { browserData, fileData, resultData, testData };

}
_renderBody(resultData) {

if (!resultData.passed && resultData.info === undefined) {
return html`
<p>An error occurred that prevented a visual-diff snapshot from being taken:</p>
<pre>${resultData.error}</pre>
`;
}

const noChanges = html`<div class="no-changes">No changes</div>`;
const goldenImage = html`<img src="../${resultData.info.golden.path}" loading="lazy" alt="">`;
const newImage = html`<img src="../${resultData.info.new.path}" loading="lazy" alt="">`;

const overlay = (this.showOverlay && !resultData.passed) ?
html`<div class="overlay"><img src="../${resultData.info.diff}" loading="lazy" alt=""></div>` : nothing;

if (this.mode === 'sideBySide') {
const g = html`<div class="diff-container">${goldenImage}</div>`;
return html`
<div class="side-by-side">
${ resultData.passed ? noChanges : g }
<div class="diff-container overlay-container">${newImage}${overlay}</div>
</div>`;
} else {
return html`
<div>
<div class="diff-container overlay-container">
${(this.mode === 'oneUpOriginal') ? goldenImage : newImage}
${overlay}
</div>
</div>`;
}

}
}

customElements.define('d2l-vdiff-report-test-result', TestResult);
15 changes: 15 additions & 0 deletions src/server/rollup.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { rollupPluginHTML as html } from '@web/rollup-plugin-html';
import { join } from 'path';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import { PATHS } from './visual-diff-plugin.js';

export default {
input: join(PATHS.VDIFF_ROOT, './report/temp/index.html'),
output: {
dir: join(PATHS.VDIFF_ROOT, 'report')
},
plugins: [
html(),
nodeResolve()
],
};
Loading

0 comments on commit ff072d0

Please sign in to comment.