- Next.js 14+ with App Router and TypeScript - Tailwind CSS and ShadCN UI styling - Zustand state management - Dexie.js for IndexedDB (local-first data) - Auth.js v5 for authentication - BMAD framework integration Co-Authored-By: Claude <noreply@anthropic.com>
80 lines
2.8 KiB
TypeScript
80 lines
2.8 KiB
TypeScript
|
|
import { expect } from '@playwright/test';
|
|
import { test } from '../support/fixtures/offline.fixture';
|
|
|
|
test.describe('Offline Action Queueing', () => {
|
|
test('should enqueue SAVE_DRAFT action when offline', async ({ offlineControl, page }) => {
|
|
// GIVEN: App is loaded
|
|
await page.goto('/');
|
|
await page.waitForFunction(() => (window as any).db !== undefined && (window as any).DraftService !== undefined, { timeout: 10000 });
|
|
|
|
// GIVEN: App goes offline
|
|
await offlineControl.goOffline(page.context());
|
|
|
|
// WHEN: DraftService.saveDraft is called (Service Integration)
|
|
await page.evaluate(async () => {
|
|
// @ts-ignore
|
|
await window.DraftService.saveDraft({
|
|
title: 'Offline Draft',
|
|
content: 'Content written offline',
|
|
tags: [],
|
|
createdAt: Date.now(),
|
|
status: 'draft',
|
|
sessionId: 'test-session'
|
|
});
|
|
});
|
|
|
|
// THEN: 'syncQueue' table has 1 item
|
|
const queueCount = await page.evaluate(async () => {
|
|
// @ts-ignore
|
|
return await window.db.syncQueue.count();
|
|
});
|
|
|
|
expect(queueCount).toBe(1);
|
|
|
|
const firstItem = await page.evaluate(async () => {
|
|
// @ts-ignore
|
|
return await window.db.syncQueue.orderBy('createdAt').first();
|
|
});
|
|
expect(firstItem.action).toBe('saveDraft');
|
|
expect(firstItem.payload.draftData.title).toBe('Offline Draft');
|
|
});
|
|
|
|
test('should enqueue DELETE_ENTRY action when offline', async ({ offlineControl, page }) => {
|
|
// GIVEN: App is loaded and has an entry
|
|
await page.goto('/');
|
|
await page.waitForFunction(() => (window as any).db !== undefined && (window as any).DraftService !== undefined, { timeout: 10000 });
|
|
|
|
// Setup entry
|
|
const draftId = await page.evaluate(async () => {
|
|
// @ts-ignore
|
|
const id = await window.db.drafts.add({
|
|
title: 'To Delete',
|
|
content: 'Delete me',
|
|
tags: [],
|
|
createdAt: Date.now(),
|
|
status: 'draft',
|
|
sessionId: 'test-session'
|
|
});
|
|
return id;
|
|
});
|
|
|
|
await offlineControl.goOffline(page.context());
|
|
|
|
// WHEN: DraftService.deleteDraft is called
|
|
await page.evaluate(async (id) => {
|
|
// @ts-ignore
|
|
await window.DraftService.deleteDraft(id);
|
|
}, draftId);
|
|
|
|
// THEN: SyncQueue has delete action
|
|
const lastItem = await page.evaluate(async () => {
|
|
// @ts-ignore
|
|
return await window.db.syncQueue.orderBy('createdAt').last();
|
|
});
|
|
|
|
expect(lastItem.action).toBe('deleteDraft');
|
|
expect(lastItem.payload.draftId).toBe(draftId);
|
|
});
|
|
});
|