diff --git a/src/lib/components/migration/EditorChooserModal.svelte b/src/lib/components/migration/EditorChooserModal.svelte
index 5532dcca..05dadf62 100644
--- a/src/lib/components/migration/EditorChooserModal.svelte
+++ b/src/lib/components/migration/EditorChooserModal.svelte
@@ -1,8 +1,9 @@
@@ -27,11 +19,11 @@
target="_blank"
class="col-start-1 row-start-1 flex items-center justify-center gap-4 no-underline">
- Limited time: 10% off Mermaid Advanced Editor with code NEWYEAR
+ Try Mermaid Advanced Editor — OSS users get 10% off with code JS26
diff --git a/src/lib/util/promos/promo.ts b/src/lib/util/promos/promo.ts
index 3656162d..6af7b2e6 100644
--- a/src/lib/util/promos/promo.ts
+++ b/src/lib/util/promos/promo.ts
@@ -5,7 +5,7 @@ import type { Component } from 'svelte';
import { get, writable, type Writable } from 'svelte/store';
import { localStorage, persist } from '../persist';
import April2025 from './April2025.svelte';
-import NewYear2026 from './NewYear2026.svelte';
+import JS2026 from './JS2026.svelte';
dayjs.extend(duration);
@@ -17,10 +17,10 @@ interface Promotion {
}
const promotions: Record = {
- 'promo-newyear-2026': {
+ 'promo-js-2026': {
startDate: new Date('2026-01-01'),
- endDate: new Date('2026-03-14'),
- component: NewYear2026,
+ endDate: new Date('2026-12-31'),
+ component: JS2026,
hideDurationMs: dayjs.duration(1, 'week').asMilliseconds()
},
'promo-april-2025': {
diff --git a/src/lib/util/util.ts b/src/lib/util/util.ts
index fc0d071a..c252f370 100644
--- a/src/lib/util/util.ts
+++ b/src/lib/util/util.ts
@@ -40,6 +40,23 @@ export const MCBaseURL = env.isEnabledMermaidChartLinks
? 'https://mermaid.ai' // 'http://localhost:5174'
: 'https://example.com';
+export const getCheckoutUrl = ({
+ utmCampaign,
+ utmMedium
+}: {
+ utmCampaign: string;
+ utmMedium: string;
+}): string => {
+ const params = new URLSearchParams({
+ coupon: 'arDfyFT8',
+ tier: 'plus',
+ utm_campaign: utmCampaign,
+ utm_medium: utmMedium,
+ utm_source: getUTMSource()
+ });
+ return `${MCBaseURL}/app/user/billing/checkout?${params.toString()}`;
+};
+
let count = 0;
export const errorDebug = (limit = 1000) => {
count += 1;
diff --git a/tests/actions.spec.ts b/tests/actions.spec.ts
index baca1a78..1dd703ad 100644
--- a/tests/actions.spec.ts
+++ b/tests/actions.spec.ts
@@ -8,8 +8,7 @@ test.describe('Check actions', () => {
test('should update markdown code', async ({ editPage }) => {
const oldText = await editPage.markdownInput.inputValue();
await editPage.typeInEditor('C --> HistoryTest', { bottom: true, newline: true });
- const newText = await editPage.markdownInput.inputValue();
- expect(newText).not.toBe(oldText);
+ await expect(editPage.markdownInput).not.toHaveValue(oldText);
});
test.skip('should load gists from URL', async ({ page }) => {
@@ -21,15 +20,15 @@ test.describe('Check actions', () => {
});
test('should download png and svg', async ({ editPage }) => {
- const firstPngSize = await editPage.checkAndDownloadPNG(80_000);
- const firstSvgSize = await editPage.downloadSVG(30_000);
+ const firstPngSize = await editPage.checkAndDownloadPNG(20_000);
+ const firstSvgSize = await editPage.downloadSVG(10_000);
// Verify downloaded file is different for different diagrams
await editPage.toggleSampleDiagrams();
await editPage.loadSampleDiagram('Entity Relationship');
- const secondPngSize = await editPage.checkAndDownloadPNG(81_000);
- const secondSvgSize = await editPage.downloadSVG(40_000);
+ const secondPngSize = await editPage.checkAndDownloadPNG(20_000);
+ const secondSvgSize = await editPage.downloadSVG(10_000);
// Verify files are actually different
expect(firstPngSize).not.toBe(secondPngSize);
diff --git a/tests/diagramUpdate.spec.ts b/tests/diagramUpdate.spec.ts
index 08a1d778..ad761ae4 100644
--- a/tests/diagramUpdate.spec.ts
+++ b/tests/diagramUpdate.spec.ts
@@ -4,6 +4,9 @@ test.describe('Auto sync tests', () => {
test('should automatically defer rendering when complex diagrams are edited', async ({
editPage
}) => {
+ test.slow(); // Complex diagram rendering can be slow under parallel load
+ // Wait for the default diagram to fully render before typing
+ await editPage.checkTextInView('Christmas');
await editPage.typeInEditor(
`
A & B & C & D & E --> F & G & H & I & J & K
@@ -27,9 +30,12 @@ A & B & C & D & E --> F & G & H & I & J & K & LongTest`
});
test('supports editing code when code is incorrect', async ({ editPage }) => {
+ test.slow(); // Error display has a 5s debounce; rendering pipeline adds more delay under load
await editPage.start(
'/edit#pako:eNpljjEKwzAMRa8SNOcEnlt6gK5eVFvYJsgOqkwpIXevg9smEE1PnyfxF3DFExgISW-CczQ2D21cYU7a-SGYXRwyvTp9jUhuKlVP-eHy7zA-leQsMEmg_QOM0BLG5FujZVMsaCQmC6ahR5ks2Lw2r84ela4-aREwKpVGwKrl_s7ut3fnkjAIcg_XDzuaUhs'
);
+ // Wait for the gitGraph code to load in the editor before typing
+ await editPage.checkInEditor('gitGraph');
await editPage.typeInEditor('branch test', { bottom: true, newline: true });
await editPage.checkTextNotInView('test');
await editPage.checkInEditor('branch test');
diff --git a/tests/test.ts b/tests/test.ts
index 2d49438d..8a90894a 100644
--- a/tests/test.ts
+++ b/tests/test.ts
@@ -71,7 +71,9 @@ export class EditorPage {
}
async checkError(text: string) {
- await expect(this.page.getByTestId(TID.errorContainer)).toContainText(text);
+ await expect(this.page.getByTestId(TID.errorContainer)).toContainText(text, {
+ timeout: 10_000
+ });
}
async checkInEditor(text: string) {
diff --git a/tests/utils.ts b/tests/utils.ts
index 3886d4bf..ec1a4516 100644
--- a/tests/utils.ts
+++ b/tests/utils.ts
@@ -42,7 +42,6 @@ export async function verifyFileSizeGreaterThan(
if (!path) throw new Error('Download path not available');
const fileSize = statSync(path).size;
expect(fileSize).toBeGreaterThan(size);
- expect(fileSize).toBeLessThan(size * 2);
return fileSize;
}