From f0951d575f5303ae19219e1d4ad0b93f70bb158b Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Wed, 17 Jan 2024 18:53:57 +0530 Subject: [PATCH 01/11] feat: Adaptive auto sync --- package.json | 2 ++ src/lib/components/View.svelte | 18 +++++++++++++++- src/lib/util/autoSync.ts | 33 +++++++++++++++++++++++++++++ src/lib/util/state.ts | 38 +++++++++++----------------------- yarn.lock | 12 +++++++++++ 5 files changed, 76 insertions(+), 27 deletions(-) create mode 100644 src/lib/util/autoSync.ts diff --git a/package.json b/package.json index 044f27a3..d65c0939 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "@sveltejs/kit": "2.3.0", "@sveltejs/vite-plugin-svelte": "^3.0.1", "@testing-library/svelte": "4.0.5", + "@types/lodash-es": "^4.17.12", "@types/pako": "2.0.3", "@types/uuid": "9.0.7", "@typescript-eslint/eslint-plugin": "6.18.1", @@ -73,6 +74,7 @@ "daisyui": "2.52.0", "dayjs": "^1.11.7", "js-base64": "3.7.5", + "lodash-es": "^4.17.21", "mermaid": "10.6.1", "monaco-editor": "0.45.0", "pako": "2.1.0", diff --git a/src/lib/components/View.svelte b/src/lib/components/View.svelte index 14e97e31..4fc22983 100644 --- a/src/lib/components/View.svelte +++ b/src/lib/components/View.svelte @@ -7,6 +7,7 @@ import { cmdKey } from '$lib/util/util'; import { render as renderDiagram } from '$lib/util/mermaid'; import type { MermaidConfig } from 'mermaid'; + import { recordRenderTime, shouldRefreshView } from '$lib/util/autoSync'; let code = ''; let config = ''; @@ -59,6 +60,7 @@ }; const handleStateChange = async (state: ValidatedState) => { + const startTime = Date.now(); if (state.error !== undefined) { error = true; errorLines = state.error.toString().split('\n'); @@ -76,6 +78,12 @@ if (code === state.code && config === state.mermaid && panZoomEnabled === state.panZoom) { return; } + + if (!shouldRefreshView()) { + outOfSync = true; + return; + } + code = state.code; config = state.mermaid; panZoomEnabled = state.panZoom; @@ -114,6 +122,10 @@ console.error('view fail', error_); error = true; } + const timeTaken = Date.now() - startTime; + recordRenderTime(timeTaken, () => { + $inputStateStore.updateDiagram = true; + }); }; onMount(() => { @@ -140,7 +152,11 @@ {/each} {:else} Diagram out of sync.
- Press (Sync button) or {cmdKey} + Enter to sync. + {#if $stateStore.autoSync} + It will be updated automatically. + {:else} + Press (Sync button) or {cmdKey} + Enter to sync. + {/if} {/if} {/if} diff --git a/src/lib/util/autoSync.ts b/src/lib/util/autoSync.ts new file mode 100644 index 00000000..336f4a0b --- /dev/null +++ b/src/lib/util/autoSync.ts @@ -0,0 +1,33 @@ +import debounce from 'lodash-es/debounce'; +import { get } from 'svelte/store'; +import { stateStore } from './state'; + +let shouldSync = true; +let updater: () => void; +const renderDelay = 1000; +const slowRenderThreshold = 250; + +const debouncedRender = debounce(() => { + shouldSync = true; + updater(); +}, renderDelay); + +export const recordRenderTime = (renderTimeMs: number, updaterFunction: () => void): void => { + const { autoSync } = get(stateStore); + if (!autoSync) { + return; + } + updater = updaterFunction; + const isSlow = renderTimeMs > slowRenderThreshold; + if (!shouldSync) { + debouncedRender(); + } + shouldSync = !isSlow; +}; + +export const shouldRefreshView = (): boolean => { + if (!shouldSync) { + debouncedRender(); + } + return shouldSync; +}; diff --git a/src/lib/util/state.ts b/src/lib/util/state.ts index 2cff194e..77e03755 100644 --- a/src/lib/util/state.ts +++ b/src/lib/util/state.ts @@ -1,12 +1,12 @@ -import { writable, get, type Readable, derived } from 'svelte/store'; -import { persist, localStorage } from './persist'; -import { saveStatistics, countLines } from './stats'; -import { serializeState, deserializeState } from './serde'; -import { cmdKey, errorDebug, formatJSON } from './util'; -import { parse } from './mermaid'; - import type { ErrorHash, MarkerData, State, ValidatedState } from '$lib/types'; +import { debounce } from 'lodash-es'; import type { MermaidConfig } from 'mermaid'; +import { derived, get, writable, type Readable } from 'svelte/store'; +import { parse } from './mermaid'; +import { localStorage, persist } from './persist'; +import { deserializeState, serializeState } from './serde'; +import { saveStatistics } from './stats'; +import { errorDebug, formatJSON } from './util'; export const defaultState: State = { code: `flowchart TD @@ -134,7 +134,6 @@ export const updateCodeStore = (newState: Partial): void => { }); }; -let prompted = false; export const updateCode = ( code: string, { @@ -142,21 +141,8 @@ export const updateCode = ( resetPanZoom = false }: { updateDiagram?: boolean; resetPanZoom?: boolean } = {} ): void => { - // console.log('updateCode', code); - const lines = countLines(code); saveStatistics(code); errorDebug(); - if (lines > 50 && !prompted && get(stateStore).autoSync) { - const turnOff = confirm( - `Long diagram detected. Turn off Auto Sync? Use ${cmdKey} + Enter or click the sync logo to manually sync.` - ); - prompted = true; - if (turnOff) { - updateCodeStore({ - autoSync: false - }); - } - } inputStateStore.update((state) => { if (resetPanZoom) { @@ -185,13 +171,13 @@ export const toggleDarkTheme = (dark: boolean): void => { }); }; -let urlDebounce: number; export const initURLSubscription = (): void => { + const updateHash = debounce((hash) => { + history.replaceState(undefined, '', `#${hash}`); + }, 250); + stateStore.subscribe(({ serialized }) => { - clearTimeout(urlDebounce); - urlDebounce = window.setTimeout(() => { - history.replaceState(undefined, '', `#${serialized}`); - }, 250); + updateHash(serialized); }); }; diff --git a/yarn.lock b/yarn.lock index d857e7b1..f4dda4b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -557,6 +557,18 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== +"@types/lodash-es@^4.17.12": + version "4.17.12" + resolved "https://registry.yarnpkg.com/@types/lodash-es/-/lodash-es-4.17.12.tgz#65f6d1e5f80539aa7cfbfc962de5def0cf4f341b" + integrity sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ== + dependencies: + "@types/lodash" "*" + +"@types/lodash@*": + version "4.14.202" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.202.tgz#f09dbd2fb082d507178b2f2a5c7e74bd72ff98f8" + integrity sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ== + "@types/mdast@^3.0.0": version "3.0.12" resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.12.tgz#beeb511b977c875a5b0cc92eab6fcac2f0895514" From e6e2b62e82af53b29ccdf3e029ab84da73937725 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Wed, 17 Jan 2024 22:28:43 +0530 Subject: [PATCH 02/11] test: Verify adaptive auto sync --- cypress/e2e/diagramUpdate.spec.ts | 18 ++++++++++ src/lib/components/Editor.svelte | 2 +- src/lib/components/View.svelte | 4 ++- src/lib/util/autoSync.ts | 2 +- src/lib/util/state.ts | 2 -- src/lib/util/stats.ts | 56 +++++++++++++++++++------------ src/lib/util/util.ts | 2 +- 7 files changed, 58 insertions(+), 28 deletions(-) diff --git a/cypress/e2e/diagramUpdate.spec.ts b/cypress/e2e/diagramUpdate.spec.ts index d2ca433c..b8326bc3 100644 --- a/cypress/e2e/diagramUpdate.spec.ts +++ b/cypress/e2e/diagramUpdate.spec.ts @@ -50,6 +50,24 @@ describe('Auto sync tests', () => { cy.getLocalStorage('codeStore').snapshot(); }); + it('should automatically defer rendering when complex diagrams are edited', () => { + cy.get('#view').should('not.have.class', 'outOfSync'); + typeInEditor(` +A & B & C & D & E --> F & G & K & Z & i +A & B & C & D & E --> F & G & K & Z & i +A & B & C & D & E --> F & G & K & Z & i +A & B & C & D & E --> F & G & K & Z & i +A & B & C & D & E --> F & G & K & Z & i +A & B & C & D & E --> F & G & K & Z & i +A & B & C & D & E --> F & G & K & Z & i +A & B & C & D & E --> F & G & K & Z & i +A & B & C & D & E --> F & G & K & Z & i`); + cy.get('#view').should('have.class', 'outOfSync'); + cy.get('#errorContainer').should('contain.text', 'It will be updated automatically.'); + // The class should be removed automatically after 1 second. + cy.get('#view').should('not.have.class', 'outOfSync'); + }); + it('supports commenting code out/in', () => { cy.get('#editor').contains('Car').click(); cy.get('#editor').get('textarea').type(`${cmd}/`, { force: true }); diff --git a/src/lib/components/Editor.svelte b/src/lib/components/Editor.svelte index 77652eca..9118a3a7 100644 --- a/src/lib/components/Editor.svelte +++ b/src/lib/components/Editor.svelte @@ -88,7 +88,7 @@ } // eslint-disable-next-line @typescript-eslint/no-unsafe-call initEditor(monaco); - errorDebug(100); + errorDebug(); editor = monaco.editor.create(divElement, editorOptions); editor.onDidChangeModelContent(({ isFlush }) => { const newText = editor?.getValue(); diff --git a/src/lib/components/View.svelte b/src/lib/components/View.svelte index 4fc22983..55fa898e 100644 --- a/src/lib/components/View.svelte +++ b/src/lib/components/View.svelte @@ -3,7 +3,7 @@ import { onMount } from 'svelte'; import panzoom from 'svg-pan-zoom'; import type { State, ValidatedState } from '$lib/types'; - import { logEvent } from '$lib/util/stats'; + import { logEvent, saveStatistics } from '$lib/util/stats'; import { cmdKey } from '$lib/util/util'; import { render as renderDiagram } from '$lib/util/mermaid'; import type { MermaidConfig } from 'mermaid'; @@ -123,6 +123,8 @@ error = true; } const timeTaken = Date.now() - startTime; + console.log({ timeTaken }); + saveStatistics(code, timeTaken); recordRenderTime(timeTaken, () => { $inputStateStore.updateDiagram = true; }); diff --git a/src/lib/util/autoSync.ts b/src/lib/util/autoSync.ts index 336f4a0b..0927ddb3 100644 --- a/src/lib/util/autoSync.ts +++ b/src/lib/util/autoSync.ts @@ -5,7 +5,7 @@ import { stateStore } from './state'; let shouldSync = true; let updater: () => void; const renderDelay = 1000; -const slowRenderThreshold = 250; +const slowRenderThreshold = 150; const debouncedRender = debounce(() => { shouldSync = true; diff --git a/src/lib/util/state.ts b/src/lib/util/state.ts index 77e03755..3c719e06 100644 --- a/src/lib/util/state.ts +++ b/src/lib/util/state.ts @@ -5,7 +5,6 @@ import { derived, get, writable, type Readable } from 'svelte/store'; import { parse } from './mermaid'; import { localStorage, persist } from './persist'; import { deserializeState, serializeState } from './serde'; -import { saveStatistics } from './stats'; import { errorDebug, formatJSON } from './util'; export const defaultState: State = { @@ -141,7 +140,6 @@ export const updateCode = ( resetPanZoom = false }: { updateDiagram?: boolean; resetPanZoom?: boolean } = {} ): void => { - saveStatistics(code); errorDebug(); inputStateStore.update((state) => { diff --git a/src/lib/util/stats.ts b/src/lib/util/stats.ts index 82d1c5fc..04183c90 100644 --- a/src/lib/util/stats.ts +++ b/src/lib/util/stats.ts @@ -47,33 +47,45 @@ export const countLines = (code: string): number => { return (code.match(/\n/g)?.length ?? 0) + 1; }; -export const saveStatistics = (graph: string): void => { +export const saveStatistics = (graph: string, renderTime: number): void => { const graphType = detectType(graph); if (!graphType) { return; } const length = countLines(graph); - const lengthBucket = - length < 10 - ? '0-10' - : length < 25 - ? '10-25' - : length < 50 - ? '25-50' - : length < 100 - ? '50-100' - : length < 200 - ? '100-200' - : length < 500 - ? '200-500' - : length < 700 - ? '500-700' - : length < 1000 - ? '700-1000' - : length < 1500 - ? '1000-1500' - : '1500+'; - logEvent('render', { graphType, length, lengthBucket }); + const lengthBucket = getBucket(length); + const renderTimeMsBucket = getBucket(renderTime); + logEvent('render', { graphType, length, lengthBucket, renderTimeMsBucket }); +}; + +const getBucket = (length: number): string => { + return length < 10 + ? '0-10' + : length < 25 + ? '10-25' + : length < 50 + ? '25-50' + : length < 100 + ? '50-100' + : length < 200 + ? '100-200' + : length < 500 + ? '200-500' + : length < 700 + ? '500-700' + : length < 1000 + ? '700-1000' + : length < 1500 + ? '1000-1500' + : length < 2500 + ? '1500-2500' + : length < 4500 + ? '2500-4500' + : length < 7000 + ? '4500-7000' + : length < 10_000 + ? '7000-10000' + : '10000+'; }; const minutesToMilliSeconds = (minutes: number): number => { diff --git a/src/lib/util/util.ts b/src/lib/util/util.ts index d082a703..65769ff5 100644 --- a/src/lib/util/util.ts +++ b/src/lib/util/util.ts @@ -28,7 +28,7 @@ export const isMac = navigator.platform.toUpperCase().includes('MAC'); export const cmdKey = isMac ? 'Cmd' : 'Ctrl'; let count = 0; -export const errorDebug = (limit = 100) => { +export const errorDebug = (limit = 1000) => { count += 1; if (count > limit) { console.log(count, limit); From d9eba9ceec8bf23d39b2454fea9fea5b6e31749e Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Thu, 18 Jan 2024 19:17:05 +0530 Subject: [PATCH 03/11] Bump cypress --- package.json | 2 +- yarn.lock | 34 ++++++++++++++-------------------- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index d65c0939..0bc54fb8 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "chai": "^4.3.7", "cssnano": "^6.0.0", "cy-verify-downloads": "0.2.2", - "cypress": "12.17.4", + "cypress": "13.6.3", "cypress-localstorage-commands": "2.2.5", "eslint": "8.56.0", "eslint-config-prettier": "9.1.0", diff --git a/yarn.lock b/yarn.lock index f4dda4b4..99c1f0e7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -69,10 +69,10 @@ resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== -"@cypress/request@2.88.12": - version "2.88.12" - resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.12.tgz#ba4911431738494a85e93fb04498cb38bc55d590" - integrity sha512-tOn+0mDZxASFM+cuAP9szGUGPI1HwWVSvdzm7V4cCsPdFTx6qMj29CwaQmRAMIEhORIUBFBsYROYJcveK4uOjA== +"@cypress/request@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@cypress/request/-/request-3.0.1.tgz#72d7d5425236a2413bd3d8bb66d02d9dc3168960" + integrity sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ== dependencies: aws-sign2 "~0.7.0" aws4 "^1.8.0" @@ -87,7 +87,7 @@ json-stringify-safe "~5.0.1" mime-types "~2.1.19" performance-now "^2.1.0" - qs "~6.10.3" + qs "6.10.4" safe-buffer "^5.1.2" tough-cookie "^4.1.3" tunnel-agent "^0.6.0" @@ -588,11 +588,6 @@ dependencies: undici-types "~5.26.4" -"@types/node@^16.18.39": - version "16.18.40" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.40.tgz#968d64746d20cac747a18ca982c0f1fe518c031c" - integrity sha512-+yno3ItTEwGxXiS/75Q/aHaa5srkpnJaH+kdkTVJ3DtJEwv92itpKbxU+FjPoh2m/5G9zmUQfrL4A4C13c+iGA== - "@types/normalize-package-data@^2.4.0": version "2.4.1" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" @@ -1671,14 +1666,13 @@ cypress-localstorage-commands@2.2.5: resolved "https://registry.yarnpkg.com/cypress-localstorage-commands/-/cypress-localstorage-commands-2.2.5.tgz#81c8f53a06e2ed93c1e068c1101da85f80f27f61" integrity sha512-07zpwzWdY+uPi1NEHFhWQNylIJqRxR78Ile05L6WT8h1Gz0OaxgBSZRuzp+pqUni/3Pk4d2ieq/cSh++ZmujEA== -cypress@12.17.4: - version "12.17.4" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.17.4.tgz#b4dadf41673058493fa0d2362faa3da1f6ae2e6c" - integrity sha512-gAN8Pmns9MA5eCDFSDJXWKUpaL3IDd89N9TtIupjYnzLSmlpVr+ZR+vb4U/qaMp+lB6tBvAmt7504c3Z4RU5KQ== +cypress@13.6.3: + version "13.6.3" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.6.3.tgz#54f03ca07ee56b2bc18211e7bd32abd2533982ba" + integrity sha512-d/pZvgwjAyZsoyJ3FOsJT5lDsqnxQ/clMqnNc++rkHjbkkiF2h9s0JsZSyyH4QXhVFW3zPFg82jD25roFLOdZA== dependencies: - "@cypress/request" "2.88.12" + "@cypress/request" "^3.0.0" "@cypress/xvfb" "^1.2.4" - "@types/node" "^16.18.39" "@types/sinonjs__fake-timers" "8.1.1" "@types/sizzle" "^2.3.2" arch "^2.2.0" @@ -5040,10 +5034,10 @@ punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== -qs@~6.10.3: - version "6.10.5" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.5.tgz#974715920a80ff6a262264acd2c7e6c2a53282b4" - integrity sha512-O5RlPh0VFtR78y79rgcgKK4wbAI0C5zGVLztOIdpWX6ep368q5Hv6XRxDvXuZ9q3C6v+e3n8UfZZJw7IIG27eQ== +qs@6.10.4: + version "6.10.4" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.4.tgz#6a3003755add91c0ec9eacdc5f878b034e73f9e7" + integrity sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g== dependencies: side-channel "^1.0.4" From 45d41b55ebeacfca11b771df646dc81e050eca47 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Thu, 18 Jan 2024 20:16:40 +0530 Subject: [PATCH 04/11] Remove use of cy.clock --- cypress.config.js | 24 +++++++++++++----------- cypress/e2e/actions.spec.ts | 4 ---- cypress/e2e/util.ts | 34 ++++++++++++++-------------------- cypress/snapshots.js | 2 +- cypress/support/e2e.js | 1 - cypress/tsconfig.json | 2 +- package.json | 1 - src/lib/components/View.svelte | 1 - yarn.lock | 5 ----- 9 files changed, 29 insertions(+), 45 deletions(-) diff --git a/cypress.config.js b/cypress.config.js index 7ebe9ef4..8b8edd92 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -1,6 +1,6 @@ import { defineConfig } from 'cypress'; import fs from 'fs'; -import { isFileExist, findFiles } from 'cy-verify-downloads'; +import path from 'path'; export default defineConfig({ projectId: '2ckppp', viewportWidth: 1440, @@ -15,17 +15,19 @@ export default defineConfig({ e2e: { setupNodeEvents(on, config) { on('task', { - isFileExist, - findFiles, - deleteFile(path) { - fs.rmSync(path); - return null; - }, - readFileMaybe(filename) { - if (fs.existsSync(filename)) { - return fs.readFileSync(filename, 'utf8'); + readAndDeleteFile({ fileNamePattern, folder, mode }) { + const fileNameRegex = new RegExp(fileNamePattern); + const files = fs.readdirSync(folder); + const filename = files.find((file) => file.match(fileNameRegex)); + const filePath = path.join(folder, filename); + try { + if (mode === 'size') { + return fs.statSync(filePath).size; + } + return fs.readFileSync(filePath, 'utf8'); + } finally { + fs.rmSync(filePath); } - return null; } }); }, diff --git a/cypress/e2e/actions.spec.ts b/cypress/e2e/actions.spec.ts index 4206f022..24d8d1b1 100644 --- a/cypress/e2e/actions.spec.ts +++ b/cypress/e2e/actions.spec.ts @@ -26,8 +26,6 @@ describe('Check actions', () => { }); it('should download png and svg', () => { - cy.clock(new Date(2022, 0, 1).getTime()); - cy.get(`#downloadPNG`).click(); verifyFileSizeGreaterThan('diagram', 'png', 34_000); @@ -43,7 +41,5 @@ describe('Check actions', () => { cy.get(`#downloadSVG`).click(); verifyFileSizeGreaterThan('diagram', 'svg', 11_000); - - cy.clock().invoke('restore'); }); }); diff --git a/cypress/e2e/util.ts b/cypress/e2e/util.ts index 456ae585..b6e29a3d 100644 --- a/cypress/e2e/util.ts +++ b/cypress/e2e/util.ts @@ -29,17 +29,14 @@ export const verifyFileSizeGreaterThan = ( extension: string, size: number ) => { - const fileName = `mermaid-${fileType}-2022-01-01-000000.${extension}`; - const filePath = `${downloadsFolder}/${fileName}`; - // eslint-disable-next-line @typescript-eslint/no-unsafe-call - cy.verifyDownload(fileName); - cy.readFile(filePath, null, { - log: false - }).then((buffer: ArrayBuffer) => { - expect(buffer.byteLength).to.be.gt(size); - expect(buffer.byteLength).to.be.lt(size * 1.3); + cy.task('readAndDeleteFile', { + folder: downloadsFolder, + fileNamePattern: `mermaid-${fileType}-.*.${extension}`, + mode: 'size' + }).then((fileSize: number) => { + expect(fileSize).to.be.gt(size); + expect(fileSize).to.be.lt(size * 1.3); }); - cy.task('deleteFile', filePath); }; export const verifyFileSnapshot = ( @@ -47,14 +44,11 @@ export const verifyFileSnapshot = ( extension: string, content: string ) => { - const fileName = `mermaid-${fileType}-2022-01-01-000000.${extension}`; - const filePath = `${downloadsFolder}/${fileName}`; - // eslint-disable-next-line @typescript-eslint/no-unsafe-call - cy.verifyDownload(fileName); - cy.readFile(filePath, null, { - log: false - }).then((buffer: ArrayBuffer) => - expect(new TextDecoder('utf8').decode(buffer)).to.contain(content) - ); - cy.task('deleteFile', filePath); + cy.task('readAndDeleteFile', { + folder: downloadsFolder, + fileNamePattern: `mermaid-${fileType}-.*.${extension}`, + mode: 'content' + }).then((fileContent: number) => { + expect(fileContent).to.contain(content); + }); }; diff --git a/cypress/snapshots.js b/cypress/snapshots.js index 350eafa3..2afc5934 100644 --- a/cypress/snapshots.js +++ b/cypress/snapshots.js @@ -16,7 +16,7 @@ module.exports = { "1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping!!)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello world\\\"\\n}\",\"autoSync\":true,\"updateDiagram\":true,\"loader\":{\"type\":\"files\",\"config\":{\"codeURL\":\"https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/code.mmd\",\"configURL\":\"https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/config.json\"}}}" } }, - "__version": "12.17.4", + "__version": "13.6.3", "Auto sync tests": { "should dim diagram when code is edited": { "1": "{\"code\":\"flowchart TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n C --> Test\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"autoSync\":false,\"updateDiagram\":false}" diff --git a/cypress/support/e2e.js b/cypress/support/e2e.js index d9401131..4ef4ffaa 100644 --- a/cypress/support/e2e.js +++ b/cypress/support/e2e.js @@ -15,7 +15,6 @@ // Import commands.js using ES2015 syntax: import './commands'; -require('cy-verify-downloads').addCustomCommand(); // Alternatively you can use CommonJS syntax: // require('./commands') diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json index edd14f8b..e9f59358 100644 --- a/cypress/tsconfig.json +++ b/cypress/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "allowJs": true, - "types": ["cypress", "cypress-localstorage-commands", "cy-verify-downloads", "node"] + "types": ["cypress", "cypress-localstorage-commands", "node"] }, "include": ["**/*.ts"] } diff --git a/package.json b/package.json index f4e982e8..181ffdaf 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,6 @@ "c8": "7.14.0", "chai": "^4.3.7", "cssnano": "^6.0.0", - "cy-verify-downloads": "0.2.2", "cypress": "13.6.3", "cypress-localstorage-commands": "2.2.5", "eslint": "8.56.0", diff --git a/src/lib/components/View.svelte b/src/lib/components/View.svelte index 55fa898e..db9efa1f 100644 --- a/src/lib/components/View.svelte +++ b/src/lib/components/View.svelte @@ -123,7 +123,6 @@ error = true; } const timeTaken = Date.now() - startTime; - console.log({ timeTaken }); saveStatistics(code, timeTaken); recordRenderTime(timeTaken, () => { $inputStateStore.updateDiagram = true; diff --git a/yarn.lock b/yarn.lock index 6a0172a9..728f3beb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1651,11 +1651,6 @@ cssstyle@^3.0.0: dependencies: rrweb-cssom "^0.6.0" -cy-verify-downloads@0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/cy-verify-downloads/-/cy-verify-downloads-0.2.2.tgz#c5eb96724f1abdaf3456a3ca5669030be52da01c" - integrity sha512-Cr4U38xg5z8AK8XiYuEcbAy1rfgtkZTdUFTWkG5NM+BxKSjRlpGt6LpylLpHp9B6ikKCBPotpeewW7fOuBFKlA== - cypress-localstorage-commands@2.2.5: version "2.2.5" resolved "https://registry.yarnpkg.com/cypress-localstorage-commands/-/cypress-localstorage-commands-2.2.5.tgz#81c8f53a06e2ed93c1e068c1101da85f80f27f61" From 3e457fb0bb055fd4fb8df5b4ea772477772c66e7 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Thu, 18 Jan 2024 20:26:26 +0530 Subject: [PATCH 05/11] Revert "Bump cypress" This reverts commit d9eba9ceec8bf23d39b2454fea9fea5b6e31749e. --- cypress/snapshots.js | 2 +- package.json | 2 +- yarn.lock | 34 ++++++++++++++++++++-------------- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/cypress/snapshots.js b/cypress/snapshots.js index 2afc5934..350eafa3 100644 --- a/cypress/snapshots.js +++ b/cypress/snapshots.js @@ -16,7 +16,7 @@ module.exports = { "1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping!!)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello world\\\"\\n}\",\"autoSync\":true,\"updateDiagram\":true,\"loader\":{\"type\":\"files\",\"config\":{\"codeURL\":\"https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/code.mmd\",\"configURL\":\"https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/config.json\"}}}" } }, - "__version": "13.6.3", + "__version": "12.17.4", "Auto sync tests": { "should dim diagram when code is edited": { "1": "{\"code\":\"flowchart TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n C --> Test\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"autoSync\":false,\"updateDiagram\":false}" diff --git a/package.json b/package.json index 181ffdaf..8263db5c 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "c8": "7.14.0", "chai": "^4.3.7", "cssnano": "^6.0.0", - "cypress": "13.6.3", + "cypress": "12.17.4", "cypress-localstorage-commands": "2.2.5", "eslint": "8.56.0", "eslint-config-prettier": "9.1.0", diff --git a/yarn.lock b/yarn.lock index 728f3beb..ca480c3e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -69,10 +69,10 @@ resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== -"@cypress/request@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@cypress/request/-/request-3.0.1.tgz#72d7d5425236a2413bd3d8bb66d02d9dc3168960" - integrity sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ== +"@cypress/request@2.88.12": + version "2.88.12" + resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.12.tgz#ba4911431738494a85e93fb04498cb38bc55d590" + integrity sha512-tOn+0mDZxASFM+cuAP9szGUGPI1HwWVSvdzm7V4cCsPdFTx6qMj29CwaQmRAMIEhORIUBFBsYROYJcveK4uOjA== dependencies: aws-sign2 "~0.7.0" aws4 "^1.8.0" @@ -87,7 +87,7 @@ json-stringify-safe "~5.0.1" mime-types "~2.1.19" performance-now "^2.1.0" - qs "6.10.4" + qs "~6.10.3" safe-buffer "^5.1.2" tough-cookie "^4.1.3" tunnel-agent "^0.6.0" @@ -588,6 +588,11 @@ dependencies: undici-types "~5.26.4" +"@types/node@^16.18.39": + version "16.18.40" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.40.tgz#968d64746d20cac747a18ca982c0f1fe518c031c" + integrity sha512-+yno3ItTEwGxXiS/75Q/aHaa5srkpnJaH+kdkTVJ3DtJEwv92itpKbxU+FjPoh2m/5G9zmUQfrL4A4C13c+iGA== + "@types/normalize-package-data@^2.4.0": version "2.4.1" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" @@ -1656,13 +1661,14 @@ cypress-localstorage-commands@2.2.5: resolved "https://registry.yarnpkg.com/cypress-localstorage-commands/-/cypress-localstorage-commands-2.2.5.tgz#81c8f53a06e2ed93c1e068c1101da85f80f27f61" integrity sha512-07zpwzWdY+uPi1NEHFhWQNylIJqRxR78Ile05L6WT8h1Gz0OaxgBSZRuzp+pqUni/3Pk4d2ieq/cSh++ZmujEA== -cypress@13.6.3: - version "13.6.3" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.6.3.tgz#54f03ca07ee56b2bc18211e7bd32abd2533982ba" - integrity sha512-d/pZvgwjAyZsoyJ3FOsJT5lDsqnxQ/clMqnNc++rkHjbkkiF2h9s0JsZSyyH4QXhVFW3zPFg82jD25roFLOdZA== +cypress@12.17.4: + version "12.17.4" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.17.4.tgz#b4dadf41673058493fa0d2362faa3da1f6ae2e6c" + integrity sha512-gAN8Pmns9MA5eCDFSDJXWKUpaL3IDd89N9TtIupjYnzLSmlpVr+ZR+vb4U/qaMp+lB6tBvAmt7504c3Z4RU5KQ== dependencies: - "@cypress/request" "^3.0.0" + "@cypress/request" "2.88.12" "@cypress/xvfb" "^1.2.4" + "@types/node" "^16.18.39" "@types/sinonjs__fake-timers" "8.1.1" "@types/sizzle" "^2.3.2" arch "^2.2.0" @@ -5024,10 +5030,10 @@ punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== -qs@6.10.4: - version "6.10.4" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.4.tgz#6a3003755add91c0ec9eacdc5f878b034e73f9e7" - integrity sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g== +qs@~6.10.3: + version "6.10.5" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.5.tgz#974715920a80ff6a262264acd2c7e6c2a53282b4" + integrity sha512-O5RlPh0VFtR78y79rgcgKK4wbAI0C5zGVLztOIdpWX6ep368q5Hv6XRxDvXuZ9q3C6v+e3n8UfZZJw7IIG27eQ== dependencies: side-channel "^1.0.4" From 71cf898f5cf5a90713f5470df86e959b81c88b80 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Thu, 18 Jan 2024 20:32:05 +0530 Subject: [PATCH 06/11] Wait for diagram to sync --- cypress/e2e/util.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/cypress/e2e/util.ts b/cypress/e2e/util.ts index b6e29a3d..41904040 100644 --- a/cypress/e2e/util.ts +++ b/cypress/e2e/util.ts @@ -29,6 +29,7 @@ export const verifyFileSizeGreaterThan = ( extension: string, size: number ) => { + cy.get('#view').should('not.have.class', 'outOfSync'); cy.task('readAndDeleteFile', { folder: downloadsFolder, fileNamePattern: `mermaid-${fileType}-.*.${extension}`, From 0c7d48970c6fed3ebb76c315ee12e117068f4433 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Thu, 18 Jan 2024 20:48:32 +0530 Subject: [PATCH 07/11] Strict match filename --- cypress/e2e/util.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cypress/e2e/util.ts b/cypress/e2e/util.ts index 41904040..2f76f625 100644 --- a/cypress/e2e/util.ts +++ b/cypress/e2e/util.ts @@ -32,7 +32,7 @@ export const verifyFileSizeGreaterThan = ( cy.get('#view').should('not.have.class', 'outOfSync'); cy.task('readAndDeleteFile', { folder: downloadsFolder, - fileNamePattern: `mermaid-${fileType}-.*.${extension}`, + fileNamePattern: `^mermaid-${fileType}-.*.${extension}$`, mode: 'size' }).then((fileSize: number) => { expect(fileSize).to.be.gt(size); @@ -47,7 +47,7 @@ export const verifyFileSnapshot = ( ) => { cy.task('readAndDeleteFile', { folder: downloadsFolder, - fileNamePattern: `mermaid-${fileType}-.*.${extension}`, + fileNamePattern: `^mermaid-${fileType}-.*.${extension}$`, mode: 'content' }).then((fileContent: number) => { expect(fileContent).to.contain(content); From 675af38c492afde558b002923592ca03fbacdbd7 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Thu, 18 Jan 2024 21:05:33 +0530 Subject: [PATCH 08/11] Throw error if out of sync --- src/lib/components/Actions.svelte | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/components/Actions.svelte b/src/lib/components/Actions.svelte index 9b1fd242..ea98a74d 100644 --- a/src/lib/components/Actions.svelte +++ b/src/lib/components/Actions.svelte @@ -31,6 +31,9 @@ }; const exportImage = (event: Event, exporter: Exporter) => { + if (document.querySelector('.outOfSync')) { + throw new Error('Diagram is out of sync'); + } const canvas: HTMLCanvasElement = document.createElement('canvas'); const svg = document.querySelector('#container svg'); if (!svg) { From 2033f1add0cd595acb83c8ab319e349fafa58f1f Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Thu, 18 Jan 2024 21:24:42 +0530 Subject: [PATCH 09/11] Wait to sync before export --- src/lib/components/Actions.svelte | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/lib/components/Actions.svelte b/src/lib/components/Actions.svelte index ea98a74d..1304e06e 100644 --- a/src/lib/components/Actions.svelte +++ b/src/lib/components/Actions.svelte @@ -30,7 +30,14 @@ return toBase64(svgString); }; - const exportImage = (event: Event, exporter: Exporter) => { + const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + + const exportImage = async (event: Event, exporter: Exporter) => { + let tries = 50; + while (document.querySelector('.outOfSync') && tries > 0) { + await sleep(100); + tries--; + } if (document.querySelector('.outOfSync')) { throw new Error('Diagram is out of sync'); } From 2acb84415af2d9e27cf41987e057d8831bc56925 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Thu, 18 Jan 2024 23:20:21 +0530 Subject: [PATCH 10/11] feat: waitForRender --- src/lib/components/Actions.svelte | 19 +++++++------------ src/lib/util/autoSync.ts | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/lib/components/Actions.svelte b/src/lib/components/Actions.svelte index 1304e06e..3cd45914 100644 --- a/src/lib/components/Actions.svelte +++ b/src/lib/components/Actions.svelte @@ -1,12 +1,13 @@