T013
This commit is contained in:
@@ -35,5 +35,6 @@
|
|||||||
"jsdom": "^25.0.0",
|
"jsdom": "^25.0.0",
|
||||||
"@testing-library/svelte": "^5.0.0",
|
"@testing-library/svelte": "^5.0.0",
|
||||||
"@testing-library/jest-dom": "^6.4.2"
|
"@testing-library/jest-dom": "^6.4.2"
|
||||||
|
,"@types/node": "^20.16.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
import { defineConfig, devices } from '@playwright/test';
|
import { defineConfig, devices } from '@playwright/test';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import { dirname } from 'path';
|
||||||
|
|
||||||
|
// ESM-compatible __dirname
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = dirname(__filename);
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
testDir: './tests/e2e',
|
testDir: './tests/e2e',
|
||||||
@@ -11,7 +17,8 @@ export default defineConfig({
|
|||||||
trace: 'on-first-retry'
|
trace: 'on-first-retry'
|
||||||
},
|
},
|
||||||
webServer: {
|
webServer: {
|
||||||
command: 'pnpm preview',
|
// Build then preview to ensure static output exists
|
||||||
|
command: 'pnpm build && pnpm preview',
|
||||||
cwd: __dirname,
|
cwd: __dirname,
|
||||||
port: 4173,
|
port: 4173,
|
||||||
reuseExistingServer: !process.env.CI
|
reuseExistingServer: !process.env.CI
|
||||||
|
|||||||
179
apps/web/tests/e2e/smoke.export-import.spec.ts
Normal file
179
apps/web/tests/e2e/smoke.export-import.spec.ts
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
// Helper to capture a coarse, implementation-agnostic grid fingerprint
|
||||||
|
// We use data attributes if present; otherwise fall back to textContent/HTML
|
||||||
|
async function captureGridFingerprint(page: import('@playwright/test').Page) {
|
||||||
|
const tiles = page.locator('[data-testid="day-tile"]');
|
||||||
|
const count = await tiles.count();
|
||||||
|
const max = Math.min(count, 60); // limit to first ~2 months worth to keep payload small
|
||||||
|
const data: Array<Record<string, string | number | null>> = [];
|
||||||
|
for (let i = 0; i < max; i++) {
|
||||||
|
const t = tiles.nth(i);
|
||||||
|
const handle = await t.elementHandle();
|
||||||
|
if (!handle) continue;
|
||||||
|
const entry = await page.evaluate((el) => {
|
||||||
|
const attr = (name: string) => el.getAttribute(name);
|
||||||
|
const selCount = (sel: string) => el.querySelectorAll(sel).length;
|
||||||
|
return {
|
||||||
|
idx: (el as HTMLElement).dataset['index'] ?? String(i),
|
||||||
|
date: attr('data-date') ?? null,
|
||||||
|
net: attr('data-net-score') ?? null,
|
||||||
|
hue: attr('data-mood-hue') ?? null,
|
||||||
|
posGlyphs: selCount('[data-testid="positive-glyphs"] [data-testid="tick"]'),
|
||||||
|
negGlyphs: selCount('[data-testid="negative-glyphs"] [data-testid="dot"]'),
|
||||||
|
aria: el.getAttribute('aria-label'),
|
||||||
|
};
|
||||||
|
}, handle);
|
||||||
|
data.push(entry);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
test.describe('Export/Import JSON roundtrip', () => {
|
||||||
|
test('creates days, exports JSON, clears DB, imports JSON, grid identical', async ({ page, context, browserName }) => {
|
||||||
|
await page.goto('/');
|
||||||
|
await expect(page.getByRole('heading', { level: 1 })).toHaveText(/GlowTrack/i);
|
||||||
|
|
||||||
|
// Ensure at least one tile is present
|
||||||
|
const firstTile = page.locator('[data-testid="day-tile"]').first();
|
||||||
|
await expect(firstTile).toBeVisible();
|
||||||
|
|
||||||
|
// Step 1: Create a couple of day entries to have non-empty state
|
||||||
|
// Day 1: +Exercise, mood hue ~ 120
|
||||||
|
await firstTile.click();
|
||||||
|
const hueInput = page.locator('[data-testid="mood-hue-slider"]');
|
||||||
|
if (await hueInput.isVisible()) {
|
||||||
|
await hueInput.fill('120');
|
||||||
|
}
|
||||||
|
const addPos = page.locator('[data-testid="add-positive-habit"]');
|
||||||
|
if (await addPos.isVisible()) {
|
||||||
|
await addPos.click();
|
||||||
|
const habitInput = page.locator('[data-testid="habit-input"]');
|
||||||
|
if (await habitInput.isVisible()) {
|
||||||
|
await habitInput.fill('Exercise');
|
||||||
|
await page.keyboard.press('Enter');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const save = page.locator('[data-testid="save-day"]');
|
||||||
|
const close = page.locator('[data-testid="close-editor"]');
|
||||||
|
if (await save.isVisible()) {
|
||||||
|
await save.click();
|
||||||
|
} else if (await close.isVisible()) {
|
||||||
|
await close.click();
|
||||||
|
} else {
|
||||||
|
await page.click('body');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Day 2: -Procrastination
|
||||||
|
const secondTile = page.locator('[data-testid="day-tile"]').nth(1);
|
||||||
|
if (await secondTile.isVisible()) {
|
||||||
|
await secondTile.click();
|
||||||
|
const addNeg = page.locator('[data-testid="add-negative-habit"]');
|
||||||
|
if (await addNeg.isVisible()) {
|
||||||
|
await addNeg.click();
|
||||||
|
const habitInput = page.locator('[data-testid="habit-input"]');
|
||||||
|
if (await habitInput.isVisible()) {
|
||||||
|
await habitInput.fill('Procrastination');
|
||||||
|
await page.keyboard.press('Enter');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (await save.isVisible()) {
|
||||||
|
await save.click();
|
||||||
|
} else if (await close.isVisible()) {
|
||||||
|
await close.click();
|
||||||
|
} else {
|
||||||
|
await page.click('body');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capture fingerprint BEFORE export
|
||||||
|
const before = await captureGridFingerprint(page);
|
||||||
|
expect(before.length).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
// Step 2: Export JSON
|
||||||
|
const exportBtn = page.locator('[data-testid="export-json"], button:has-text("Export JSON"), [aria-label="Export JSON"]');
|
||||||
|
await expect(exportBtn).toBeVisible();
|
||||||
|
const downloadPromise = page.waitForEvent('download');
|
||||||
|
await exportBtn.click();
|
||||||
|
const download = await downloadPromise;
|
||||||
|
const suggested = download.suggestedFilename();
|
||||||
|
const filePath = await download.path();
|
||||||
|
expect(filePath).toBeTruthy();
|
||||||
|
// We don't parse here to avoid Node type deps; presence of a file is enough.
|
||||||
|
|
||||||
|
// Step 3: Clear IndexedDB and any cached state, then reload
|
||||||
|
await page.evaluate(async () => {
|
||||||
|
try {
|
||||||
|
// Best-effort clear for known DB name; ignore errors
|
||||||
|
const deleteDb = (name: string) => new Promise<void>((res) => { const req = indexedDB.deleteDatabase(name); req.onsuccess = () => res(); req.onerror = () => res(); req.onblocked = () => res(); });
|
||||||
|
try { await deleteDb('glowtrack'); } catch {}
|
||||||
|
// Attempt to enumerate all DBs if supported
|
||||||
|
// @ts-ignore - databases() is not in older TS DOM libs
|
||||||
|
const dbs = (await indexedDB.databases?.()) || [];
|
||||||
|
for (const db of dbs) {
|
||||||
|
if (db && db.name) {
|
||||||
|
try { await deleteDb(db.name); } catch {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
try { localStorage.clear(); } catch {}
|
||||||
|
try { sessionStorage.clear(); } catch {}
|
||||||
|
// Clear any caches (PWA)
|
||||||
|
try {
|
||||||
|
const keys = await caches.keys();
|
||||||
|
await Promise.all(keys.map((k) => caches.delete(k)));
|
||||||
|
} catch {}
|
||||||
|
});
|
||||||
|
await page.reload();
|
||||||
|
await expect(page.getByRole('heading', { level: 1 })).toHaveText(/GlowTrack/i);
|
||||||
|
|
||||||
|
// Expect state to be different after clearing (very likely empty/default)
|
||||||
|
const afterClear = await captureGridFingerprint(page);
|
||||||
|
// If app shows an empty grid with same number of tiles and no attributes,
|
||||||
|
// at least one of the first two tiles should differ by net/hue/glyphs
|
||||||
|
let differs = false;
|
||||||
|
const minLen = Math.min(before.length, afterClear.length);
|
||||||
|
for (let i = 0; i < Math.min(minLen, 2); i++) {
|
||||||
|
const a = before[i];
|
||||||
|
const b = afterClear[i];
|
||||||
|
if (a.net !== b.net || a.hue !== b.hue || a.posGlyphs !== b.posGlyphs || a.negGlyphs !== b.negGlyphs) {
|
||||||
|
differs = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect(differs).toBeTruthy();
|
||||||
|
|
||||||
|
// Step 4: Import the previously exported JSON
|
||||||
|
const importBtn = page.locator('[data-testid="import-json"], button:has-text("Import JSON"), [aria-label="Import JSON"]');
|
||||||
|
await expect(importBtn).toBeVisible();
|
||||||
|
|
||||||
|
// Prefer setting a hidden file input directly if present
|
||||||
|
const input = page.locator('input[type="file"][accept*="json"], input[type="file"][data-testid="import-file-input"]');
|
||||||
|
if (await input.count()) {
|
||||||
|
await input.first().setInputFiles(filePath!);
|
||||||
|
} else {
|
||||||
|
const chooserPromise = page.waitForEvent('filechooser');
|
||||||
|
await importBtn.click();
|
||||||
|
const chooser = await chooserPromise;
|
||||||
|
await chooser.setFiles(filePath!);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Give the app a moment to process the import and update UI
|
||||||
|
await page.waitForTimeout(250);
|
||||||
|
|
||||||
|
// Step 5: Verify the grid fingerprint matches the one before export
|
||||||
|
const afterImport = await captureGridFingerprint(page);
|
||||||
|
|
||||||
|
// Compare shallowly for first N records
|
||||||
|
const n = Math.min(before.length, afterImport.length, 30);
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
const a = before[i];
|
||||||
|
const b = afterImport[i];
|
||||||
|
expect(b.net).toBe(a.net);
|
||||||
|
expect(b.hue).toBe(a.hue);
|
||||||
|
expect(b.posGlyphs).toBe(a.posGlyphs);
|
||||||
|
expect(b.negGlyphs).toBe(a.negGlyphs);
|
||||||
|
// aria and date are optional comparisons
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
87
pnpm-lock.yaml
generated
87
pnpm-lock.yaml
generated
@@ -35,13 +35,13 @@ importers:
|
|||||||
version: 1.55.0
|
version: 1.55.0
|
||||||
'@sveltejs/adapter-static':
|
'@sveltejs/adapter-static':
|
||||||
specifier: ^3.0.0
|
specifier: ^3.0.0
|
||||||
version: 3.0.9(@sveltejs/kit@2.42.1(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.20))(svelte@4.2.20)(vite@5.4.20))
|
version: 3.0.9(@sveltejs/kit@2.42.1(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.20(@types/node@20.19.17)))(svelte@4.2.20)(vite@5.4.20(@types/node@20.19.17)))
|
||||||
'@sveltejs/kit':
|
'@sveltejs/kit':
|
||||||
specifier: ^2.5.0
|
specifier: ^2.5.0
|
||||||
version: 2.42.1(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.20))(svelte@4.2.20)(vite@5.4.20)
|
version: 2.42.1(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.20(@types/node@20.19.17)))(svelte@4.2.20)(vite@5.4.20(@types/node@20.19.17))
|
||||||
'@sveltejs/vite-plugin-svelte':
|
'@sveltejs/vite-plugin-svelte':
|
||||||
specifier: ^3.0.2
|
specifier: ^3.0.2
|
||||||
version: 3.1.2(svelte@4.2.20)(vite@5.4.20)
|
version: 3.1.2(svelte@4.2.20)(vite@5.4.20(@types/node@20.19.17))
|
||||||
'@tailwindcss/forms':
|
'@tailwindcss/forms':
|
||||||
specifier: ^0.5.9
|
specifier: ^0.5.9
|
||||||
version: 0.5.10(tailwindcss@3.4.17)
|
version: 0.5.10(tailwindcss@3.4.17)
|
||||||
@@ -50,7 +50,10 @@ importers:
|
|||||||
version: 6.8.0
|
version: 6.8.0
|
||||||
'@testing-library/svelte':
|
'@testing-library/svelte':
|
||||||
specifier: ^5.0.0
|
specifier: ^5.0.0
|
||||||
version: 5.2.8(svelte@4.2.20)(vite@5.4.20)(vitest@2.1.9(@vitest/ui@2.1.9)(jsdom@25.0.1))
|
version: 5.2.8(svelte@4.2.20)(vite@5.4.20(@types/node@20.19.17))(vitest@2.1.9(@types/node@20.19.17)(@vitest/ui@2.1.9)(jsdom@25.0.1))
|
||||||
|
'@types/node':
|
||||||
|
specifier: ^20.16.11
|
||||||
|
version: 20.19.17
|
||||||
autoprefixer:
|
autoprefixer:
|
||||||
specifier: ^10.4.20
|
specifier: ^10.4.20
|
||||||
version: 10.4.21(postcss@8.5.6)
|
version: 10.4.21(postcss@8.5.6)
|
||||||
@@ -71,10 +74,10 @@ importers:
|
|||||||
version: 5.9.2
|
version: 5.9.2
|
||||||
vite:
|
vite:
|
||||||
specifier: ^5.1.0
|
specifier: ^5.1.0
|
||||||
version: 5.4.20
|
version: 5.4.20(@types/node@20.19.17)
|
||||||
vitest:
|
vitest:
|
||||||
specifier: ^2.1.1
|
specifier: ^2.1.1
|
||||||
version: 2.1.9(@vitest/ui@2.1.9)(jsdom@25.0.1)
|
version: 2.1.9(@types/node@20.19.17)(@vitest/ui@2.1.9)(jsdom@25.0.1)
|
||||||
|
|
||||||
packages/storage:
|
packages/storage:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -90,7 +93,7 @@ importers:
|
|||||||
version: 5.9.2
|
version: 5.9.2
|
||||||
vitest:
|
vitest:
|
||||||
specifier: ^2.1.1
|
specifier: ^2.1.1
|
||||||
version: 2.1.9(@vitest/ui@2.1.9)(jsdom@25.0.1)
|
version: 2.1.9(@types/node@20.19.17)(@vitest/ui@2.1.9)(jsdom@25.0.1)
|
||||||
|
|
||||||
packages/viz:
|
packages/viz:
|
||||||
devDependencies:
|
devDependencies:
|
||||||
@@ -105,7 +108,7 @@ importers:
|
|||||||
version: 5.9.2
|
version: 5.9.2
|
||||||
vitest:
|
vitest:
|
||||||
specifier: ^2.1.1
|
specifier: ^2.1.1
|
||||||
version: 2.1.9(@vitest/ui@2.1.9)(jsdom@25.0.1)
|
version: 2.1.9(@types/node@20.19.17)(@vitest/ui@2.1.9)(jsdom@25.0.1)
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
@@ -554,6 +557,9 @@ packages:
|
|||||||
'@types/estree@1.0.8':
|
'@types/estree@1.0.8':
|
||||||
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
|
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
|
||||||
|
|
||||||
|
'@types/node@20.19.17':
|
||||||
|
resolution: {integrity: sha512-gfehUI8N1z92kygssiuWvLiwcbOB3IRktR6hTDgJlXMYh5OvkPSRmgfoBUmfZt+vhwJtX7v1Yw4KvvAf7c5QKQ==}
|
||||||
|
|
||||||
'@types/pug@2.0.10':
|
'@types/pug@2.0.10':
|
||||||
resolution: {integrity: sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==}
|
resolution: {integrity: sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==}
|
||||||
|
|
||||||
@@ -1743,6 +1749,9 @@ packages:
|
|||||||
engines: {node: '>=14.17'}
|
engines: {node: '>=14.17'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
undici-types@6.21.0:
|
||||||
|
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
|
||||||
|
|
||||||
update-browserslist-db@1.1.3:
|
update-browserslist-db@1.1.3:
|
||||||
resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==}
|
resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -2163,15 +2172,15 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
acorn: 8.15.0
|
acorn: 8.15.0
|
||||||
|
|
||||||
'@sveltejs/adapter-static@3.0.9(@sveltejs/kit@2.42.1(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.20))(svelte@4.2.20)(vite@5.4.20))':
|
'@sveltejs/adapter-static@3.0.9(@sveltejs/kit@2.42.1(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.20(@types/node@20.19.17)))(svelte@4.2.20)(vite@5.4.20(@types/node@20.19.17)))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sveltejs/kit': 2.42.1(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.20))(svelte@4.2.20)(vite@5.4.20)
|
'@sveltejs/kit': 2.42.1(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.20(@types/node@20.19.17)))(svelte@4.2.20)(vite@5.4.20(@types/node@20.19.17))
|
||||||
|
|
||||||
'@sveltejs/kit@2.42.1(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.20))(svelte@4.2.20)(vite@5.4.20)':
|
'@sveltejs/kit@2.42.1(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.20(@types/node@20.19.17)))(svelte@4.2.20)(vite@5.4.20(@types/node@20.19.17))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@standard-schema/spec': 1.0.0
|
'@standard-schema/spec': 1.0.0
|
||||||
'@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0)
|
'@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0)
|
||||||
'@sveltejs/vite-plugin-svelte': 3.1.2(svelte@4.2.20)(vite@5.4.20)
|
'@sveltejs/vite-plugin-svelte': 3.1.2(svelte@4.2.20)(vite@5.4.20(@types/node@20.19.17))
|
||||||
'@types/cookie': 0.6.0
|
'@types/cookie': 0.6.0
|
||||||
acorn: 8.15.0
|
acorn: 8.15.0
|
||||||
cookie: 0.6.0
|
cookie: 0.6.0
|
||||||
@@ -2184,28 +2193,28 @@ snapshots:
|
|||||||
set-cookie-parser: 2.7.1
|
set-cookie-parser: 2.7.1
|
||||||
sirv: 3.0.2
|
sirv: 3.0.2
|
||||||
svelte: 4.2.20
|
svelte: 4.2.20
|
||||||
vite: 5.4.20
|
vite: 5.4.20(@types/node@20.19.17)
|
||||||
|
|
||||||
'@sveltejs/vite-plugin-svelte-inspector@2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.20))(svelte@4.2.20)(vite@5.4.20)':
|
'@sveltejs/vite-plugin-svelte-inspector@2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.20(@types/node@20.19.17)))(svelte@4.2.20)(vite@5.4.20(@types/node@20.19.17))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sveltejs/vite-plugin-svelte': 3.1.2(svelte@4.2.20)(vite@5.4.20)
|
'@sveltejs/vite-plugin-svelte': 3.1.2(svelte@4.2.20)(vite@5.4.20(@types/node@20.19.17))
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
svelte: 4.2.20
|
svelte: 4.2.20
|
||||||
vite: 5.4.20
|
vite: 5.4.20(@types/node@20.19.17)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.20)':
|
'@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.20(@types/node@20.19.17))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sveltejs/vite-plugin-svelte-inspector': 2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.20))(svelte@4.2.20)(vite@5.4.20)
|
'@sveltejs/vite-plugin-svelte-inspector': 2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@5.4.20(@types/node@20.19.17)))(svelte@4.2.20)(vite@5.4.20(@types/node@20.19.17))
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
deepmerge: 4.3.1
|
deepmerge: 4.3.1
|
||||||
kleur: 4.1.5
|
kleur: 4.1.5
|
||||||
magic-string: 0.30.19
|
magic-string: 0.30.19
|
||||||
svelte: 4.2.20
|
svelte: 4.2.20
|
||||||
svelte-hmr: 0.16.0(svelte@4.2.20)
|
svelte-hmr: 0.16.0(svelte@4.2.20)
|
||||||
vite: 5.4.20
|
vite: 5.4.20(@types/node@20.19.17)
|
||||||
vitefu: 0.2.5(vite@5.4.20)
|
vitefu: 0.2.5(vite@5.4.20(@types/node@20.19.17))
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
@@ -2234,13 +2243,13 @@ snapshots:
|
|||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
redent: 3.0.0
|
redent: 3.0.0
|
||||||
|
|
||||||
'@testing-library/svelte@5.2.8(svelte@4.2.20)(vite@5.4.20)(vitest@2.1.9(@vitest/ui@2.1.9)(jsdom@25.0.1))':
|
'@testing-library/svelte@5.2.8(svelte@4.2.20)(vite@5.4.20(@types/node@20.19.17))(vitest@2.1.9(@types/node@20.19.17)(@vitest/ui@2.1.9)(jsdom@25.0.1))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@testing-library/dom': 10.4.1
|
'@testing-library/dom': 10.4.1
|
||||||
svelte: 4.2.20
|
svelte: 4.2.20
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
vite: 5.4.20
|
vite: 5.4.20(@types/node@20.19.17)
|
||||||
vitest: 2.1.9(@vitest/ui@2.1.9)(jsdom@25.0.1)
|
vitest: 2.1.9(@types/node@20.19.17)(@vitest/ui@2.1.9)(jsdom@25.0.1)
|
||||||
|
|
||||||
'@types/aria-query@5.0.4': {}
|
'@types/aria-query@5.0.4': {}
|
||||||
|
|
||||||
@@ -2248,6 +2257,10 @@ snapshots:
|
|||||||
|
|
||||||
'@types/estree@1.0.8': {}
|
'@types/estree@1.0.8': {}
|
||||||
|
|
||||||
|
'@types/node@20.19.17':
|
||||||
|
dependencies:
|
||||||
|
undici-types: 6.21.0
|
||||||
|
|
||||||
'@types/pug@2.0.10': {}
|
'@types/pug@2.0.10': {}
|
||||||
|
|
||||||
'@ungap/structured-clone@1.3.0': {}
|
'@ungap/structured-clone@1.3.0': {}
|
||||||
@@ -2259,13 +2272,13 @@ snapshots:
|
|||||||
chai: 5.3.3
|
chai: 5.3.3
|
||||||
tinyrainbow: 1.2.0
|
tinyrainbow: 1.2.0
|
||||||
|
|
||||||
'@vitest/mocker@2.1.9(vite@5.4.20)':
|
'@vitest/mocker@2.1.9(vite@5.4.20(@types/node@20.19.17))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vitest/spy': 2.1.9
|
'@vitest/spy': 2.1.9
|
||||||
estree-walker: 3.0.3
|
estree-walker: 3.0.3
|
||||||
magic-string: 0.30.19
|
magic-string: 0.30.19
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
vite: 5.4.20
|
vite: 5.4.20(@types/node@20.19.17)
|
||||||
|
|
||||||
'@vitest/pretty-format@2.1.9':
|
'@vitest/pretty-format@2.1.9':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -2295,7 +2308,7 @@ snapshots:
|
|||||||
sirv: 3.0.2
|
sirv: 3.0.2
|
||||||
tinyglobby: 0.2.15
|
tinyglobby: 0.2.15
|
||||||
tinyrainbow: 1.2.0
|
tinyrainbow: 1.2.0
|
||||||
vitest: 2.1.9(@vitest/ui@2.1.9)(jsdom@25.0.1)
|
vitest: 2.1.9(@types/node@20.19.17)(@vitest/ui@2.1.9)(jsdom@25.0.1)
|
||||||
|
|
||||||
'@vitest/utils@2.1.9':
|
'@vitest/utils@2.1.9':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -3442,6 +3455,8 @@ snapshots:
|
|||||||
|
|
||||||
typescript@5.9.2: {}
|
typescript@5.9.2: {}
|
||||||
|
|
||||||
|
undici-types@6.21.0: {}
|
||||||
|
|
||||||
update-browserslist-db@1.1.3(browserslist@4.26.2):
|
update-browserslist-db@1.1.3(browserslist@4.26.2):
|
||||||
dependencies:
|
dependencies:
|
||||||
browserslist: 4.26.2
|
browserslist: 4.26.2
|
||||||
@@ -3454,13 +3469,13 @@ snapshots:
|
|||||||
|
|
||||||
util-deprecate@1.0.2: {}
|
util-deprecate@1.0.2: {}
|
||||||
|
|
||||||
vite-node@2.1.9:
|
vite-node@2.1.9(@types/node@20.19.17):
|
||||||
dependencies:
|
dependencies:
|
||||||
cac: 6.7.14
|
cac: 6.7.14
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
es-module-lexer: 1.7.0
|
es-module-lexer: 1.7.0
|
||||||
pathe: 1.1.2
|
pathe: 1.1.2
|
||||||
vite: 5.4.20
|
vite: 5.4.20(@types/node@20.19.17)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/node'
|
- '@types/node'
|
||||||
- less
|
- less
|
||||||
@@ -3472,22 +3487,23 @@ snapshots:
|
|||||||
- supports-color
|
- supports-color
|
||||||
- terser
|
- terser
|
||||||
|
|
||||||
vite@5.4.20:
|
vite@5.4.20(@types/node@20.19.17):
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild: 0.21.5
|
esbuild: 0.21.5
|
||||||
postcss: 8.5.6
|
postcss: 8.5.6
|
||||||
rollup: 4.50.2
|
rollup: 4.50.2
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
|
'@types/node': 20.19.17
|
||||||
fsevents: 2.3.3
|
fsevents: 2.3.3
|
||||||
|
|
||||||
vitefu@0.2.5(vite@5.4.20):
|
vitefu@0.2.5(vite@5.4.20(@types/node@20.19.17)):
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
vite: 5.4.20
|
vite: 5.4.20(@types/node@20.19.17)
|
||||||
|
|
||||||
vitest@2.1.9(@vitest/ui@2.1.9)(jsdom@25.0.1):
|
vitest@2.1.9(@types/node@20.19.17)(@vitest/ui@2.1.9)(jsdom@25.0.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vitest/expect': 2.1.9
|
'@vitest/expect': 2.1.9
|
||||||
'@vitest/mocker': 2.1.9(vite@5.4.20)
|
'@vitest/mocker': 2.1.9(vite@5.4.20(@types/node@20.19.17))
|
||||||
'@vitest/pretty-format': 2.1.9
|
'@vitest/pretty-format': 2.1.9
|
||||||
'@vitest/runner': 2.1.9
|
'@vitest/runner': 2.1.9
|
||||||
'@vitest/snapshot': 2.1.9
|
'@vitest/snapshot': 2.1.9
|
||||||
@@ -3503,10 +3519,11 @@ snapshots:
|
|||||||
tinyexec: 0.3.2
|
tinyexec: 0.3.2
|
||||||
tinypool: 1.1.1
|
tinypool: 1.1.1
|
||||||
tinyrainbow: 1.2.0
|
tinyrainbow: 1.2.0
|
||||||
vite: 5.4.20
|
vite: 5.4.20(@types/node@20.19.17)
|
||||||
vite-node: 2.1.9
|
vite-node: 2.1.9(@types/node@20.19.17)
|
||||||
why-is-node-running: 2.3.0
|
why-is-node-running: 2.3.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
|
'@types/node': 20.19.17
|
||||||
'@vitest/ui': 2.1.9(vitest@2.1.9)
|
'@vitest/ui': 2.1.9(vitest@2.1.9)
|
||||||
jsdom: 25.0.1
|
jsdom: 25.0.1
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ Integration scenarios from quickstart.md → e2e smoke tests [P]
|
|||||||
- Steps: open app → set day mood → add positive+negative habits → tile glow/luminance and glyphs update
|
- Steps: open app → set day mood → add positive+negative habits → tile glow/luminance and glyphs update
|
||||||
- Dependencies: T007, T005
|
- Dependencies: T007, T005
|
||||||
|
|
||||||
- [ ] T013 [P] E2E: export/import JSON roundtrip
|
- [X] T013 [P] E2E: export/import JSON roundtrip
|
||||||
- Create /home/jawz/Development/Projects/GlowTrack/apps/web/tests/e2e/smoke.export-import.spec.ts
|
- Create /home/jawz/Development/Projects/GlowTrack/apps/web/tests/e2e/smoke.export-import.spec.ts
|
||||||
- Steps: create few days → export JSON → clear DB → import JSON → grid identical
|
- Steps: create few days → export JSON → clear DB → import JSON → grid identical
|
||||||
- Dependencies: T007, T005
|
- Dependencies: T007, T005
|
||||||
|
|||||||
Reference in New Issue
Block a user