@@ -7,9 +7,9 @@ This project uses **Playwright** for all testing needs, with a focused test suit
77## Test Results
88
99``` bash
10- ✅ 7 passing tests (15s )
10+ ✅ 12 passing tests (~22s )
1111 - 3 unit tests (pure utility functions)
12- - 4 integration tests (complete user flows)
12+ - 9 integration tests (complete user flows)
1313```
1414
1515## Architecture
@@ -74,6 +74,18 @@ The main integration test (`complete-flow.test.ts`) provides **high confidence**
7474- Verifies ` useZendeskIframeStyles ` hook injects custom styles
7575- Confirms ` <style data-zendesk-custom-styles="true"> ` exists in iframe
7676
77+ ** 6. ✅ Clear Conversation Data Flow**
78+
79+ - Tests the "Clear Conversation Data" button functionality
80+ - Verifies confirmation dialog appears with proper content
81+ - Tests dialog cancellation (storage preserved, dialog closes)
82+ - Confirms storage (localStorage/sessionStorage) is cleared on confirmation
83+ - Verifies user returns to consent screen after clearing
84+ - Tests dialog keyboard interactions (Escape key, Tab navigation, focus management)
85+ - Tests overlay click to close dialog
86+ - Verifies widget is not rendered after state reset
87+ - Verifies button visibility based on widget state
88+
7789### Example Test Output
7890
7991```
@@ -94,6 +106,9 @@ The main integration test (`complete-flow.test.ts`) provides **high confidence**
94106 - https://improving.duckduckgo.com/t/subscriptionsupport_helplink_getting-started
95107 - https://improving.duckduckgo.com/t/subscriptionsupport_link_ticket
96108 - https://improving.duckduckgo.com/t/subscriptionsupport_helpful_yes
109+
110+ ✅ Storage cleared and widget not rendered
111+ ✅ Dialog cancelled and storage preserved
97112```
98113
99114## Running Tests
@@ -227,6 +242,7 @@ zE('messenger', 'render', { mode: 'embedded', widget: { targetElement: '#id' } }
227242zE('messenger:on', 'unreadMessages', callback)
228243zE('messenger:set', 'cookies', 'functional')
229244zE('messenger:set', 'customization', { theme: {...} })
245+ zE('messenger', 'resetWidget', callback) // Resets widget and calls callback
230246` ` `
231247
232248Creates actual iframe structure :
@@ -243,6 +259,12 @@ Includes realistic content:
243259- Yes/No feedback buttons for testing click handlers
244260- Bot messages and conversation structure
245261
262+ **Mock Features:**
263+
264+ - ` resetWidget` support - Resets widget state, removes iframes, and calls callback
265+ - Proper iframe structure matching production Zendesk widget
266+ - Realistic timing for callbacks and rendering
267+
246268This ensures tests verify actual hook behavior, not simulations.
247269
248270# # Test Types Explained
@@ -309,6 +331,9 @@ test('complete flow', async ({ page }) => {
309331- Style injection via `useZendeskIframeStyles` hook
310332- Pixel event tracking to `improving.duckduckgo.com`
311333- Message sending and user interactions
334+ - Clear conversation data flow with dialog confirmation
335+ - Storage clearing (localStorage/sessionStorage)
336+ - State reset and return to consent screen
312337
313338# # Writing New Tests
314339
@@ -334,37 +359,22 @@ test.describe('myUtility', () => {
334359
335360# ## Adding an Integration Test
336361
337- For testing user flows with Zendesk :
362+ For testing user flows with Zendesk, use the helper functions defined in `complete-flow.test.ts` for cleaner, more maintainable tests :
338363
339364` ` ` typescript
340365import { test, expect } from '@playwright/test';
341- import { readFileSync } from 'fs';
342- import { join } from 'path';
366+ // Helper functions are defined at the top of complete-flow.test.ts
367+ // They can be reused within that file or copied to new test files
343368
344369test('new user flow', async ({ page }) => {
345- // Load Zendesk mock
346- const mockScript = readFileSync(
347- join(__dirname, '../fixtures/zendesk-mock.js'),
348- 'utf-8',
349- );
350-
351- await page.route('https://static.zdassets.com/ekr/snippet.js*', (route) => {
352- route.fulfill({
353- status: 200,
354- contentType: 'application/javascript',
355- body: mockScript,
356- });
357- });
370+ // Set up Zendesk mock (helper function)
371+ await setupZendeskMock(page);
358372
359- // Optionally intercept pixel events
360- const pixelRequests: string[] = [];
361- await page.route('https://improving.duckduckgo.com/t/*', (route) => {
362- pixelRequests.push(route.request().url());
363- route.fulfill({ status: 200, body: '' });
364- });
373+ // Optionally intercept pixel events (helper function)
374+ const pixelRequests = await setupPixelInterception(page);
365375
366- // Test your flow
367- await page.goto('/' );
376+ // Load widget and wait for it to be ready (helper function)
377+ await loadWidget(page );
368378
369379 // ... your test steps ...
370380
@@ -373,6 +383,14 @@ test('new user flow', async ({ page }) => {
373383});
374384` ` `
375385
386+ **Helper Functions Available in `complete-flow.test.ts`:**
387+
388+ - ` setupZendeskMock(page)` - Sets up the Zendesk script mock route (mock script loaded once at module level)
389+ - ` loadWidget(page)` - Navigates to page, clicks consent button, waits for widget ready (~1500ms)
390+ - ` setupPixelInterception(page)` - Sets up pixel request interception, returns requests array
391+
392+ These helpers eliminate code duplication and ensure consistent test setup. The mock script is loaded once at module level for better performance.
393+
376394# # Troubleshooting
377395
378396# ## Tests are slow
@@ -420,6 +438,7 @@ test('new user flow', async ({ page }) => {
420438- Intercept and verify network requests (pixel events)
421439- Use the same selectors as your hooks (e.g., `ZENDESK_ARTICLE_LINK_SELECTOR`) for consistency
422440- Use `data-testid` attributes for stable element selection
441+ - Test IDs available : ` LOAD_ZD_BUTTON_TEST_ID` , `DELETE_DATA_BUTTON_TEST_ID`, `CONFIRM_DELETE_DATA_BUTTON_TEST_ID`, `CANCEL_DELETE_DATA_BUTTON_TEST_ID`
423442- Wait for specific conditions with `waitForSelector()` instead of arbitrary timeouts
424443- Test in the real Next.js app environment
425444
@@ -430,7 +449,7 @@ test('new user flow', async ({ page }) => {
430449- Don't mock what you're testing - only mock external dependencies
431450- Don't test implementation details - test user-visible behavior
432451- Don't use brittle CSS class selectors - use semantic selectors
433- - Don't duplicate test setup - use shared fixtures and helpers
452+ - Don't duplicate test setup - use shared fixtures and helper functions (`setupZendeskMock`, `loadWidget`, `setupPixelInterception`)
434453
435454# # Maintenance
436455
@@ -506,9 +525,9 @@ When you create a new hook that modifies the widget:
506525
507526 ` ` ` typescript
508527 test('should verify new hook behavior', async ({ page }) => {
509- // Setup (mock, goto, click button)
510- // Wait for hooks to activate
511- await page.waitForTimeout(1500 );
528+ // Use helper functions for setup
529+ await setupZendeskMock(page);
530+ await loadWidget(page );
512531
513532 // Verify hook's DOM changes
514533 const result = await page.evaluate(() => {
@@ -522,12 +541,58 @@ When you create a new hook that modifies the widget:
522541 });
523542 ` ` `
524543
544+ # ## Testing Clear Conversation Data Flow
545+
546+ When testing the clear conversation data functionality :
547+
548+ ` ` ` typescript
549+ test('should clear conversation data', async ({ page }) => {
550+ await setupZendeskMock(page);
551+ await loadWidget(page);
552+
553+ // Pre-populate storage
554+ await page.evaluate(() => {
555+ localStorage.setItem('test-key', 'test-value');
556+ sessionStorage.setItem('test-session-key', 'test-session-value');
557+ });
558+
559+ // Click clear button
560+ const clearButton = page.getByTestId(DELETE_DATA_BUTTON_TEST_ID);
561+ await clearButton.click();
562+
563+ // Verify dialog appears
564+ const dialog = page.getByRole('dialog');
565+ await expect(dialog).toBeVisible();
566+
567+ // Confirm deletion
568+ const confirmButton = page.getByTestId(CONFIRM_DELETE_DATA_BUTTON_TEST_ID);
569+ await confirmButton.click();
570+
571+ // Wait for reload
572+ await page.waitForLoadState('networkidle');
573+
574+ // Verify storage is cleared
575+ const storageState = await page.evaluate(() => ({
576+ localStorage: Object.keys(localStorage).length,
577+ sessionStorage: Object.keys(sessionStorage).length,
578+ }));
579+ expect(storageState.sessionStorage).toBe(0);
580+ });
581+ ` ` `
582+
525583# # Key Files
526584
527585**Tests:**
528586
529- - ` src/tests/integration/complete-flow.test.ts` - Comprehensive integration tests (4 tests)
587+ - ` src/tests/integration/complete-flow.test.ts` - Comprehensive integration tests (9 tests)
588+ - Complete widget flow with pixel tracking
589+ - Custom styles injection verification
590+ - Article link click tracking
591+ - Yes/No button click tracking
592+ - Clear conversation data flow
593+ - Dialog cancellation flow
530594- ` src/tests/unit/build-article-url.test.ts` - Unit tests for utilities (3 tests)
595+ - ` src/tests/unit/get-slug-from-url.test.ts` - Unit tests for utilities (3 tests)
531596
532597**Fixtures:**
533598
@@ -542,37 +607,35 @@ When you create a new hook that modifies the widget:
542607
543608# ## How Integration Tests Work
544609
545- 1. **Mock Setup**
610+ 1. **Mock Setup** (using helper functions)
546611
547612 ` ` ` typescript
548- // Load the mock script
549- const mockScript = readFileSync('zendesk-mock.js', 'utf-8' );
613+ // Set up Zendesk mock (helper function handles script loading)
614+ await setupZendeskMock(page );
550615
551- // Intercept Zendesk CDN request
552- await page.route('https://static.zdassets.com/ekr/snippet.js*', (route) => {
553- route.fulfill({ body: mockScript });
554- });
616+ // Optionally set up pixel interception
617+ const pixelRequests = await setupPixelInterception(page);
555618 ` ` `
556619
557- 2. **User Action**
620+ 2. **User Action** (using helper functions)
558621
559622 ` ` ` typescript
560- // Navigate and click button
561- await page.goto('/');
562- await page.getByTestId('load-zd-button').click();
623+ // Load widget (helper function handles navigation, consent click, and waiting)
624+ await loadWidget(page);
563625 ` ` `
564626
5656273. **Widget Renders**
566628
567629 ` ` ` typescript
568- // Mock creates iframes
569- await page.waitForSelector('#messaging-container iframe');
630+ // Widget is ready after loadWidget() completes
631+ // Mock creates iframes automatically
570632 ` ` `
571633
5726344. **Hooks Activate**
573635
574636 ` ` ` typescript
575- // Wait for hooks (zendeskReady + callbacks)
637+ // Hooks activate automatically (loadWidget waits for them)
638+ // Additional waits may be needed for specific hook effects
576639 await page.waitForTimeout(1500);
577640 ` ` `
578641
@@ -584,6 +647,16 @@ When you create a new hook that modifies the widget:
584647 await expect(link).toHaveAttribute('href', /duckduckgo\. com/);
585648 ` ` `
586649
650+ **Helper Functions:**
651+
652+ The test suite includes optimized helper functions to reduce code duplication :
653+
654+ - ` setupZendeskMock(page)` - Loads and routes the Zendesk mock script (loaded once at module level)
655+ - ` loadWidget(page)` - Navigates to page, clicks consent, waits for widget ready (~1500ms)
656+ - ` setupPixelInterception(page)` - Sets up pixel request interception, returns requests array
657+
658+ These helpers make tests more maintainable and consistent.
659+
587660# ## Why 1500ms Wait?
588661
589662The integration tests wait 1500ms for hooks to activate because :
@@ -606,4 +679,4 @@ For issues or questions:
606679
607680---
608681
609- **Summary:** A focused test suite providing high confidence for CI deployment. Tests the complete user journey with realistic mocks, verifying iframe rendering, link swapping, message sending, and pixel tracking.
682+ **Summary:** A focused test suite providing high confidence for CI deployment. Tests the complete user journey with realistic mocks, verifying iframe rendering, link swapping, message sending, pixel tracking, and the clear conversation data flow. Uses optimized helper functions to reduce duplication and improve maintainability .
0 commit comments