Compare commits

...

25 Commits

Author SHA1 Message Date
Sidharth Vinod
b4be53ff04 Merge branch 'develop' into sidv/runTimeDiff
* develop: (280 commits)
  chore: Remove unused imports in block
  Fix spelling
  Update docs
  Lychee ignore chrome webstore
  Update link
  chore(deps): update all patch dependencies
  build(docs): vendor CSS dependencies
  chore(deps): update all minor dependencies
  Ran lint:fix
  Fix chrome webstore url causing 404
  build(deps): update `langium` to `v3` and apply the required changes
  Resolves E2E testing issues and issue #5343
  Fix spelling
  Fix community integrations
  Fix docs
  docs: Fix config
  Update all minor dependencies
  Amend docs to document gitgraph parallel commits
  Fix lint
  Use Yarn Add COREPACK_ENABLE_STRICT
  ...
2024-03-06 14:59:32 +05:30
Sidharth Vinod
93dee55ade Merge pull request #5240 from mermaid-js/sidv/runTimeDiffTest
Test
2024-01-26 02:27:56 +05:30
Sidharth Vinod
781945325d Cleanup runTime.ts 2024-01-26 02:07:55 +05:30
Sidharth Vinod
dc476233ba Add summary 2024-01-26 01:54:09 +05:30
Sidharth Vinod
eb6c92b0d9 Remove small differences 2024-01-26 01:47:36 +05:30
Sidharth Vinod
c6cf5953a1 HTML table 2024-01-26 01:40:30 +05:30
Sidharth Vinod
32db724752 Change path 2024-01-26 01:23:30 +05:30
Sidharth Vinod
6702d41840 Change path 2024-01-26 01:15:43 +05:30
Sidharth Vinod
441deaffc9 Test 2024-01-26 01:05:59 +05:30
Sidharth Vinod
af53a968f6 feat: Record time difference in tests 2024-01-26 00:52:56 +05:30
Sidharth Vinod
3ecb841c1a Remove dummy change from e2e.yml 2024-01-24 22:17:10 +05:30
Sidharth Vinod
1d2450245e Remove dummy change 2024-01-24 22:16:21 +05:30
Sidharth Vinod
f6c4c9260f Remove log 2024-01-24 22:13:17 +05:30
Sidharth Vinod
f354d68350 Format message 2024-01-24 22:12:33 +05:30
Sidharth Vinod
bea76aa682 Format Size 2024-01-24 21:59:58 +05:30
Sidharth Vinod
6d4b27aacb Fix out format 2024-01-24 21:51:15 +05:30
Sidharth Vinod
e008b7dae7 Remove logs 2024-01-24 21:45:30 +05:30
Sidharth Vinod
96a3991c56 Update glob 2024-01-24 21:42:31 +05:30
Sidharth Vinod
8d1d691bc3 debug 2024-01-24 21:38:59 +05:30
Sidharth Vinod
e07608209b debug 2024-01-24 21:28:49 +05:30
Sidharth Vinod
50cdb74d54 Fix file path 2024-01-24 21:25:05 +05:30
Sidharth Vinod
edc091f4d4 Test 2024-01-24 21:16:10 +05:30
Sidharth Vinod
e0448a7b7b Feat: Calculate package size change on PRs 2024-01-24 21:12:16 +05:30
Sidharth Vinod
0f02f5ff34 Skip running tests 2024-01-24 21:04:57 +05:30
Sidharth Vinod
b2111adef5 Feat: Calculate package size change on PRs 2024-01-24 21:02:12 +05:30
10 changed files with 330 additions and 3 deletions

View File

@@ -88,6 +88,7 @@ rels
reqs
rewritelinks
rgba
runtimes
RIGHTOF
sankey
sequencenumber

View File

@@ -15,6 +15,7 @@ on:
permissions:
contents: read
pull-requests: write
env:
# For PRs and MergeQueues, the target commit is used, and for push events, github.event.previous is used.
@@ -48,15 +49,35 @@ jobs:
with:
ref: ${{ env.targetHash }}
- name: Install dependencies
if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }}
uses: cypress-io/github-action@v6
with:
# just perform install
runTests: false
- name: Build
if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' && github.event_name == 'pull_request' }}
run: |
pnpm run build:viz
mkdir -p cypress/snapshots/stats/base
mv stats cypress/snapshots/stats/base
- name: Cypress run
uses: cypress-io/github-action@v4
uses: cypress-io/github-action@v6
id: cypress-snapshot-gen
if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }}
with:
install: false
start: pnpm run dev
wait-on: 'http://localhost:9000'
browser: chrome
- name: Move runtime data
if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }}
run: |
mv cypress/snapshots/runtimes/current cypress/snapshots/runtimes/base
e2e:
runs-on: ubuntu-latest
container:
@@ -86,15 +107,42 @@ jobs:
path: ./cypress/snapshots
key: ${{ runner.os }}-snapshots-${{ env.targetHash }}
- name: Install dependencies
uses: cypress-io/github-action@v6
with:
runTests: false
- name: Build
id: size
if: ${{ github.event_name == 'pull_request' && matrix.containers == 1 }}
run: |
pnpm run build:viz
mv stats cypress/snapshots/stats/head
{
echo 'size_diff<<EOF'
npx tsx scripts/size.ts
echo EOF
} >> "$GITHUB_OUTPUT"
# Size diff only needs to be posted from one job, on PRs.
- name: Comment PR size difference
if: ${{ github.event_name == 'pull_request' && matrix.containers == 1 }}
uses: thollander/actions-comment-pull-request@v2
with:
message: |
${{ steps.size.outputs.size_diff }}
comment_tag: size-diff
# Install NPM dependencies, cache them correctly
# and run all Cypress tests
- name: Cypress run
uses: cypress-io/github-action@v4
uses: cypress-io/github-action@v6
id: cypress
# If CYPRESS_RECORD_KEY is set, run in parallel on all containers
# Otherwise (e.g. if running from fork), we run on a single container only
if: ${{ ( env.CYPRESS_RECORD_KEY != '' ) || ( matrix.containers == 1 ) }}
with:
install: false
start: pnpm run dev:coverage
wait-on: 'http://localhost:9000'
browser: chrome
@@ -133,6 +181,16 @@ jobs:
runs-on: ubuntu-latest
if: ${{ always() }}
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
# uses version from "packageManager" field in package.json
- name: Setup Node.js 18.x
uses: actions/setup-node@v4
with:
node-version: 18.x
# Download all snapshot artifacts and merge them into a single folder
- name: Download All Artifacts
uses: actions/download-artifact@v4
@@ -141,6 +199,27 @@ jobs:
pattern: snapshots-*
merge-multiple: true
- name: Build
id: runtime
if: ${{ needs.e2e.result != 'failure' && github.event_name == 'pull_request' }}
run: |
mv ./snapshots/runtimes/current ./snapshots/runtimes/head
npm config set ignore-scripts true
pnpm install --frozen-lockfile
{
echo 'runtime_diff<<EOF'
npx tsx scripts/runTime.ts ./snapshots
echo EOF
} >> "$GITHUB_OUTPUT"
- name: Comment PR runtime difference
if: ${{ github.event_name == 'pull_request' }}
uses: thollander/actions-comment-pull-request@v2
with:
message: |
${{ steps.runtime.outputs.runtime_diff }}
comment_tag: size-diff
# For successful push events, we save the snapshots cache
- name: Save snapshots cache
id: cache-upload

3
.gitignore vendored
View File

@@ -29,6 +29,7 @@ Gemfile.lock
cypress/screenshots/
cypress/snapshots/
cypress/runtimes/
# eslint --cache file
.eslintcache
@@ -50,4 +51,4 @@ demos/dev/**
tsx-0/**
# autogenereated by langium-cli
generated/
generated/

View File

@@ -1,4 +1,6 @@
import { defineConfig } from 'cypress';
import fs from 'fs';
import path from 'path';
import { addMatchImageSnapshotPlugin } from 'cypress-image-snapshot/plugin';
import coverage from '@cypress/code-coverage/task';
import eyesPlugin from '@applitools/eyes-cypress';
@@ -17,6 +19,19 @@ export default eyesPlugin(
}
return launchOptions;
});
on('task', {
recordRenderTime({ fileName, testName, timeTaken }) {
const resultsPath = path.join('cypress', 'snapshots', 'runtimes', 'current');
if (!fs.existsSync(resultsPath)) {
fs.mkdirSync(resultsPath, { recursive: true });
}
fs.appendFileSync(
path.join(resultsPath, `${fileName}.csv`),
`${testName},${timeTaken}\n`
);
return true;
},
});
addMatchImageSnapshotPlugin(on, config);
// copy any needed variables from process.env to config.env
config.env.useAppli = process.env.USE_APPLI ? true : false;

View File

@@ -110,6 +110,14 @@ export const openURLAndVerifyRendering = (
cy.visit(url);
cy.window().should('have.property', 'rendered', true);
cy.window().then((win) => {
cy.task('recordRenderTime', {
fileName: Cypress.spec.name,
testName: name,
// @ts-ignore Dynamically added property.
timeTaken: win.renderTime,
});
});
cy.get('svg').should('be.visible');
if (validation) {

View File

@@ -8,6 +8,7 @@ function b64ToUtf8(str) {
// Adds a rendered flag to window when rendering is done, so cypress can wait for it.
function markRendered() {
window.renderTime = Date.now() - window.loadTime;
if (window.Cypress) {
window.rendered = true;
}
@@ -131,6 +132,7 @@ if (typeof document !== 'undefined') {
window.addEventListener(
'load',
function () {
this.window.loadTime = Date.now();
if (this.location.href.match('xss.html')) {
this.console.log('Using api');
void contentLoadedApi().finally(markRendered);

View File

@@ -111,6 +111,7 @@
"jsdom": "^22.0.0",
"langium-cli": "3.0.1",
"lint-staged": "^13.2.1",
"markdown-table": "^3.0.3",
"nyc": "^15.1.0",
"path-browserify": "^1.0.1",
"pnpm": "^8.6.8",

3
pnpm-lock.yaml generated
View File

@@ -163,6 +163,9 @@ importers:
lint-staged:
specifier: ^13.2.1
version: 13.3.0
markdown-table:
specifier: ^3.0.3
version: 3.0.3
nyc:
specifier: ^15.1.0
version: 15.1.0

135
scripts/runTime.ts Normal file
View File

@@ -0,0 +1,135 @@
/* eslint-disable no-console */
import { readFile } from 'fs/promises';
import { globby } from 'globby';
interface RunTimes {
[key: string]: number;
}
interface TestResult {
[key: string]: RunTimes;
}
const getRuntimes = (csv: string): RunTimes => {
const lines = csv.split('\n');
const runtimes: RunTimes = {};
for (const line of lines) {
const [testName, timeTaken] = line.split(',');
if (testName && timeTaken) {
runtimes[testName] = Number(timeTaken);
}
}
return runtimes;
};
const readStats = async (path: string): Promise<TestResult> => {
const files = await globby(path);
const contents = await Promise.all(
files.map(async (file) => [file, await readFile(file, 'utf-8')])
);
const sizes = contents.map(([file, content]) => [file.split('/').pop(), getRuntimes(content)]);
return Object.fromEntries(sizes);
};
const percentChangeThreshold = 5;
const percentageDifference = (
oldValue: number,
newValue: number
): { change: string; crossedThreshold: boolean } => {
const difference = Math.abs(newValue - oldValue);
const avg = (newValue + oldValue) / 2;
const percentage = (difference / avg) * 100;
const roundedPercentage = percentage.toFixed(2); // Round to two decimal places
if (roundedPercentage === '0.00') {
return { change: '0.00%', crossedThreshold: false };
}
const sign = newValue > oldValue ? '+' : '-';
return {
change: `${sign}${roundedPercentage}%`,
crossedThreshold: percentage > percentChangeThreshold,
};
};
const main = async () => {
const base = process.argv[2] || './cypress/snapshots';
const oldStats = await readStats(`${base}/runtimes/base/**/*.csv`);
const newStats = await readStats(`${base}/runtimes/head/**/*.csv`);
const fullData: string[][] = [];
const changed: string[][] = [];
let oldRuntimeSum = 0;
let newRuntimeSum = 0;
let testCount = 0;
for (const [fileName, runtimes] of Object.entries(newStats)) {
const oldStat = oldStats[fileName];
if (!oldStat) {
continue;
}
for (const [testName, timeTaken] of Object.entries(runtimes)) {
const oldTimeTaken = oldStat[testName];
if (!oldTimeTaken) {
continue;
}
oldRuntimeSum += oldTimeTaken;
newRuntimeSum += timeTaken;
testCount++;
const delta = timeTaken - oldTimeTaken;
const { change, crossedThreshold } = percentageDifference(oldTimeTaken, timeTaken);
const out = [
fileName,
testName.replace('#', ''),
`${oldTimeTaken}/${timeTaken}`,
`${delta.toString()}ms ${change}`,
];
if (crossedThreshold && Math.abs(delta) > 25) {
changed.push(out);
}
fullData.push(out);
}
}
const oldAverage = oldRuntimeSum / testCount;
const newAverage = newRuntimeSum / testCount;
const { change, crossedThreshold } = percentageDifference(oldAverage, newAverage);
const headers = ['File', 'Test', 'Time Old/New', 'Change (%)'];
console.log(`## Runtime Changes
Old runtime average: ${oldAverage.toFixed(2)}ms
New runtime average: ${newAverage.toFixed(2)}ms
Change: ${change} ${crossedThreshold ? '⚠️' : ''}
`);
console.log(`
<details>
<summary>Changed tests</summary>
${htmlTable([headers, ...changed])}
</details>
`);
console.log(`
<details>
<summary>Full Data</summary>
${htmlTable([headers, ...fullData])}
</details>
`);
};
const htmlTable = (data: string[][]): string => {
let table = `<table border='1' style="border-collapse: collapse">`;
// Generate table header
table += `<tr>
${data
.shift()!
.map((header) => `<th>${header}</th>`)
.join('')}
</tr>`;
// Generate table rows
for (const row of data) {
table += `<tr>
${row.map((cell) => `<td>${cell}</td>`).join('')}
</tr>`;
}
table += '</table>';
return table;
};
void main().catch((e) => console.error(e));

82
scripts/size.ts Normal file
View File

@@ -0,0 +1,82 @@
/* eslint-disable no-console */
import type { Metafile } from 'esbuild';
import { readFile } from 'fs/promises';
import { globby } from 'globby';
import { markdownTable } from 'markdown-table';
export const getSizes = (metafile: Metafile) => {
const { outputs } = metafile;
const sizes = Object.keys(outputs)
.filter((key) => key.endsWith('js') && !key.includes('chunk'))
.map((key) => {
const { bytes } = outputs[key];
return [key.replace('dist/', ''), bytes];
});
return sizes;
};
const readStats = async (path: string): Promise<Record<string, number>> => {
const files = await globby(path);
const contents = await Promise.all(files.map((file) => readFile(file, 'utf-8')));
const sizes = contents.flatMap((content) => getSizes(JSON.parse(content)));
return Object.fromEntries(sizes);
};
const formatBytes = (bytes: number): string => {
if (bytes == 0) {
return '0 Bytes';
}
const base = 1024;
const decimals = 2;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(base));
return parseFloat((bytes / Math.pow(base, i)).toFixed(decimals)) + ' ' + sizes[i];
};
const formatSize = (bytes: number): string => {
const formatted = formatBytes(bytes);
if (formatted.includes('Bytes')) {
return formatted;
}
return `${formatBytes(bytes)} (${bytes} Bytes)`;
};
const percentageDifference = (oldValue: number, newValue: number): string => {
const difference = Math.abs(newValue - oldValue);
const avg = (newValue + oldValue) / 2;
const percentage = (difference / avg) * 100;
const roundedPercentage = percentage.toFixed(2); // Round to two decimal places
if (roundedPercentage === '0.00') {
return '0.00%';
}
const sign = newValue > oldValue ? '+' : '-';
return `${sign}${roundedPercentage}%`;
};
const main = async () => {
const oldStats = await readStats('./cypress/snapshots/stats/base/**/*.json');
const newStats = await readStats('./cypress/snapshots/stats/head/**/*.json');
const diff = Object.entries(newStats)
.filter(([, value]) => value > 2048)
.map(([key, value]) => {
const oldValue = oldStats[key];
const delta = value - oldValue;
const output = [
key,
formatSize(oldValue),
formatSize(value),
formatSize(delta),
percentageDifference(oldValue, value),
];
return output;
})
.filter(([, , , delta]) => delta !== '0 Bytes');
if (diff.length === 0) {
console.log('No changes in bundle sizes');
return;
}
console.log(
markdownTable([['File', 'Previous Size', 'New Size', 'Difference', '% Change'], ...diff])
);
};
void main().catch((e) => console.error(e));