@assertrx/dino 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/README.md +293 -0
  2. package/extensions/dino.ts +70 -0
  3. package/extensions/mabl-mcp.ts +306 -0
  4. package/extensions/playwright-mcp.ts +142 -0
  5. package/package.json +47 -0
  6. package/prompts/create-pr.md +110 -0
  7. package/prompts/generate-bdd-from-url.md +65 -0
  8. package/prompts/generate-pom-from-url-cli.md +48 -0
  9. package/prompts/generate-pom-from-url.md +45 -0
  10. package/prompts/generate-spec-from-feature.md +64 -0
  11. package/prompts/heal-playwright-failures.md +106 -0
  12. package/prompts/setup-playwright.md +81 -0
  13. package/scripts/start-dino.cmd +8 -0
  14. package/scripts/start-dino.mjs +226 -0
  15. package/scripts/start-dino.ps1 +9 -0
  16. package/scripts/start-dino.sh +10 -0
  17. package/skills/acli/SKILL.md +292 -0
  18. package/skills/bdd-gherkin/SKILL.md +552 -0
  19. package/skills/build-review-interface/SKILL.md +96 -0
  20. package/skills/error-analysis/SKILL.md +164 -0
  21. package/skills/eval-audit/SKILL.md +183 -0
  22. package/skills/evaluate-rag/SKILL.md +177 -0
  23. package/skills/generate-synthetic-data/SKILL.md +131 -0
  24. package/skills/mabl-mcp/SKILL.md +79 -0
  25. package/skills/playwright-cli/SKILL.md +246 -0
  26. package/skills/playwright-cli/advanced-workflows.md +679 -0
  27. package/skills/playwright-cli/core-commands.md +375 -0
  28. package/skills/playwright-cli/device-emulation.md +475 -0
  29. package/skills/playwright-cli/request-mocking.md +373 -0
  30. package/skills/playwright-cli/running-custom-code.md +553 -0
  31. package/skills/playwright-cli/screenshots-and-media.md +457 -0
  32. package/skills/playwright-cli/session-management.md +425 -0
  33. package/skills/playwright-cli/storage-and-auth.md +487 -0
  34. package/skills/playwright-cli/test-generation.md +336 -0
  35. package/skills/playwright-cli/tracing-and-debugging.md +501 -0
  36. package/skills/playwright-core/SKILL.md +101 -0
  37. package/skills/playwright-core/accessibility.md +1517 -0
  38. package/skills/playwright-core/angular.md +1086 -0
  39. package/skills/playwright-core/api-testing.md +1623 -0
  40. package/skills/playwright-core/assertions-and-waiting.md +703 -0
  41. package/skills/playwright-core/auth-flows.md +1081 -0
  42. package/skills/playwright-core/authentication.md +1409 -0
  43. package/skills/playwright-core/browser-apis.md +673 -0
  44. package/skills/playwright-core/browser-extensions.md +319 -0
  45. package/skills/playwright-core/canvas-and-webgl.md +494 -0
  46. package/skills/playwright-core/clock-and-time-mocking.md +427 -0
  47. package/skills/playwright-core/common-pitfalls.md +1318 -0
  48. package/skills/playwright-core/component-testing.md +1179 -0
  49. package/skills/playwright-core/configuration.md +731 -0
  50. package/skills/playwright-core/crud-testing.md +945 -0
  51. package/skills/playwright-core/debugging.md +827 -0
  52. package/skills/playwright-core/drag-and-drop.md +992 -0
  53. package/skills/playwright-core/electron-testing.md +622 -0
  54. package/skills/playwright-core/error-and-edge-cases.md +1181 -0
  55. package/skills/playwright-core/error-index.md +1903 -0
  56. package/skills/playwright-core/file-operations.md +749 -0
  57. package/skills/playwright-core/file-upload-download.md +1012 -0
  58. package/skills/playwright-core/fixtures-and-hooks.md +1072 -0
  59. package/skills/playwright-core/flaky-tests.md +883 -0
  60. package/skills/playwright-core/forms-and-validation.md +1056 -0
  61. package/skills/playwright-core/i18n-and-localization.md +622 -0
  62. package/skills/playwright-core/iframes-and-shadow-dom.md +488 -0
  63. package/skills/playwright-core/locator-strategy.md +588 -0
  64. package/skills/playwright-core/locators.md +743 -0
  65. package/skills/playwright-core/mobile-and-responsive.md +1669 -0
  66. package/skills/playwright-core/multi-context-and-popups.md +581 -0
  67. package/skills/playwright-core/multi-user-and-collaboration.md +477 -0
  68. package/skills/playwright-core/network-mocking.md +1395 -0
  69. package/skills/playwright-core/nextjs.md +1013 -0
  70. package/skills/playwright-core/performance-testing.md +608 -0
  71. package/skills/playwright-core/react.md +1082 -0
  72. package/skills/playwright-core/search-and-filter.md +1366 -0
  73. package/skills/playwright-core/security-testing.md +624 -0
  74. package/skills/playwright-core/service-workers-and-pwa.md +524 -0
  75. package/skills/playwright-core/test-architecture.md +569 -0
  76. package/skills/playwright-core/test-data-management.md +1209 -0
  77. package/skills/playwright-core/test-organization.md +947 -0
  78. package/skills/playwright-core/third-party-integrations.md +754 -0
  79. package/skills/playwright-core/trace-analysis.md +161 -0
  80. package/skills/playwright-core/visual-regression.md +1027 -0
  81. package/skills/playwright-core/vue.md +1162 -0
  82. package/skills/playwright-core/websockets-and-realtime.md +623 -0
  83. package/skills/playwright-core/when-to-mock.md +827 -0
  84. package/skills/playwright-pom/SKILL.md +94 -0
  85. package/skills/playwright-pom/page-object-model.md +921 -0
  86. package/skills/playwright-pom/pom-vs-fixtures-vs-helpers.md +937 -0
  87. package/skills/validate-evaluator/SKILL.md +215 -0
  88. package/skills/write-judge-prompt/SKILL.md +144 -0
  89. package/themes/dino.json +66 -0
@@ -0,0 +1,427 @@
1
+ # Clock and Time Mocking
2
+
3
+ > **When to use**: Testing time-dependent features -- countdown timers, scheduled events, expiration dates, age gates, session timeouts, or any UI that behaves differently based on the current time. Playwright's `page.clock` API lets you control time without waiting in real-time.
4
+ > **Prerequisites**: [core/assertions-and-waiting.md](assertions-and-waiting.md), [core/configuration.md](configuration.md)
5
+
6
+ ## Quick Reference
7
+
8
+ ```typescript
9
+ // Freeze time at a specific moment
10
+ await page.clock.install({ time: new Date('2025-03-15T10:00:00Z') });
11
+ await page.goto('/dashboard');
12
+
13
+ // Advance time by 5 minutes
14
+ await page.clock.fastForward('05:00');
15
+
16
+ // Set time to a specific point (jumps, does not tick through)
17
+ await page.clock.setFixedTime(new Date('2025-12-31T23:59:59Z'));
18
+
19
+ // Let time resume ticking from current mocked point
20
+ await page.clock.resume();
21
+ ```
22
+
23
+ **Key concept**: `page.clock.install()` replaces `Date`, `setTimeout`, `setInterval`, and `requestAnimationFrame` in the page. Call it before `page.goto()` so the page loads with mocked time from the start.
24
+
25
+ ## Patterns
26
+
27
+ ### Frozen Time with `install()` and `setFixedTime()`
28
+
29
+ **Use when**: Your test needs time to stand still at a specific moment -- verifying what the UI shows at a particular date/time.
30
+ **Avoid when**: The feature under test depends on timers ticking (use `fastForward` instead).
31
+
32
+ **TypeScript**
33
+ ```typescript
34
+ import { test, expect } from '@playwright/test';
35
+
36
+ test('dashboard shows correct greeting based on time of day', async ({ page }) => {
37
+ // Install clock BEFORE navigating
38
+ await page.clock.install({ time: new Date('2025-06-15T08:30:00') });
39
+ await page.goto('/dashboard');
40
+ await expect(page.getByText('Good morning')).toBeVisible();
41
+
42
+ // Jump to afternoon
43
+ await page.clock.setFixedTime(new Date('2025-06-15T14:00:00'));
44
+ await page.reload();
45
+ await expect(page.getByText('Good afternoon')).toBeVisible();
46
+
47
+ // Jump to evening
48
+ await page.clock.setFixedTime(new Date('2025-06-15T20:00:00'));
49
+ await page.reload();
50
+ await expect(page.getByText('Good evening')).toBeVisible();
51
+ });
52
+
53
+ test('subscription shows correct expiration status', async ({ page }) => {
54
+ // Freeze time to a date when subscription is active
55
+ await page.clock.install({ time: new Date('2025-06-01T12:00:00Z') });
56
+ await page.goto('/account');
57
+
58
+ await expect(page.getByTestId('subscription-status')).toHaveText('Active');
59
+ await expect(page.getByTestId('days-remaining')).toContainText('29');
60
+
61
+ // Jump to the expiration date
62
+ await page.clock.setFixedTime(new Date('2025-06-30T12:00:00Z'));
63
+ await page.reload();
64
+
65
+ await expect(page.getByTestId('subscription-status')).toHaveText('Expiring today');
66
+ });
67
+
68
+ test('content displays correctly on a specific holiday', async ({ page }) => {
69
+ await page.clock.install({ time: new Date('2025-12-25T10:00:00') });
70
+ await page.goto('/');
71
+
72
+ await expect(page.getByText('Happy Holidays')).toBeVisible();
73
+ await expect(page.getByTestId('holiday-banner')).toBeVisible();
74
+ });
75
+ ```
76
+
77
+ **JavaScript**
78
+ ```javascript
79
+ const { test, expect } = require('@playwright/test');
80
+
81
+ test('dashboard shows correct greeting based on time of day', async ({ page }) => {
82
+ await page.clock.install({ time: new Date('2025-06-15T08:30:00') });
83
+ await page.goto('/dashboard');
84
+ await expect(page.getByText('Good morning')).toBeVisible();
85
+
86
+ await page.clock.setFixedTime(new Date('2025-06-15T14:00:00'));
87
+ await page.reload();
88
+ await expect(page.getByText('Good afternoon')).toBeVisible();
89
+ });
90
+
91
+ test('subscription shows correct expiration status', async ({ page }) => {
92
+ await page.clock.install({ time: new Date('2025-06-01T12:00:00Z') });
93
+ await page.goto('/account');
94
+
95
+ await expect(page.getByTestId('subscription-status')).toHaveText('Active');
96
+ });
97
+ ```
98
+
99
+ ### Fast-Forwarding Time with `fastForward()`
100
+
101
+ **Use when**: Testing timers, countdowns, debounced actions, or any feature that reacts to elapsed time. `fastForward` fires all pending timers up to the specified duration.
102
+ **Avoid when**: You just need to check a static time-dependent display -- use `setFixedTime` instead.
103
+
104
+ **TypeScript**
105
+ ```typescript
106
+ import { test, expect } from '@playwright/test';
107
+
108
+ test('countdown timer reaches zero', async ({ page }) => {
109
+ await page.clock.install({ time: new Date('2025-03-15T10:00:00Z') });
110
+ await page.goto('/sale');
111
+
112
+ // Sale countdown starts at 2 hours
113
+ await expect(page.getByTestId('countdown')).toContainText('2:00:00');
114
+
115
+ // Fast-forward 1 hour
116
+ await page.clock.fastForward('01:00:00');
117
+ await expect(page.getByTestId('countdown')).toContainText('1:00:00');
118
+
119
+ // Fast-forward remaining time
120
+ await page.clock.fastForward('01:00:00');
121
+ await expect(page.getByTestId('countdown')).toContainText('0:00:00');
122
+ await expect(page.getByText('Sale ended')).toBeVisible();
123
+ });
124
+
125
+ test('auto-save triggers after 30 seconds of inactivity', async ({ page }) => {
126
+ await page.clock.install({ time: new Date('2025-03-15T10:00:00Z') });
127
+ await page.goto('/editor');
128
+
129
+ // Type something
130
+ await page.getByRole('textbox', { name: 'Content' }).fill('Draft content');
131
+
132
+ // Fast-forward past the auto-save interval
133
+ await page.clock.fastForward('00:30');
134
+
135
+ await expect(page.getByText('Saved')).toBeVisible();
136
+ });
137
+
138
+ test('session timeout warning appears after 25 minutes', async ({ page }) => {
139
+ await page.clock.install({ time: new Date('2025-03-15T10:00:00Z') });
140
+ await page.goto('/dashboard');
141
+
142
+ // Fast-forward to just before the warning (25 min)
143
+ await page.clock.fastForward('24:59');
144
+ await expect(page.getByRole('dialog', { name: 'Session timeout' })).not.toBeVisible();
145
+
146
+ // One more minute triggers the warning
147
+ await page.clock.fastForward('00:01');
148
+ await expect(page.getByRole('dialog', { name: 'Session timeout' })).toBeVisible();
149
+ await expect(page.getByText('Your session will expire in 5 minutes')).toBeVisible();
150
+ });
151
+ ```
152
+
153
+ **JavaScript**
154
+ ```javascript
155
+ const { test, expect } = require('@playwright/test');
156
+
157
+ test('countdown timer reaches zero', async ({ page }) => {
158
+ await page.clock.install({ time: new Date('2025-03-15T10:00:00Z') });
159
+ await page.goto('/sale');
160
+
161
+ await expect(page.getByTestId('countdown')).toContainText('2:00:00');
162
+
163
+ await page.clock.fastForward('01:00:00');
164
+ await expect(page.getByTestId('countdown')).toContainText('1:00:00');
165
+
166
+ await page.clock.fastForward('01:00:00');
167
+ await expect(page.getByText('Sale ended')).toBeVisible();
168
+ });
169
+
170
+ test('auto-save triggers after inactivity', async ({ page }) => {
171
+ await page.clock.install({ time: new Date('2025-03-15T10:00:00Z') });
172
+ await page.goto('/editor');
173
+
174
+ await page.getByRole('textbox', { name: 'Content' }).fill('Draft content');
175
+ await page.clock.fastForward('00:30');
176
+
177
+ await expect(page.getByText('Saved')).toBeVisible();
178
+ });
179
+ ```
180
+
181
+ ### Resuming Time with `resume()`
182
+
183
+ **Use when**: You need to start with a mocked time, then let time flow normally for interaction-dependent behavior.
184
+ **Avoid when**: The entire test should use mocked time.
185
+
186
+ **TypeScript**
187
+ ```typescript
188
+ import { test, expect } from '@playwright/test';
189
+
190
+ test('notification appears in real-time after scheduled trigger', async ({ page }) => {
191
+ // Start at a known time
192
+ await page.clock.install({ time: new Date('2025-03-15T09:59:55Z') });
193
+ await page.goto('/dashboard');
194
+
195
+ // Notification is scheduled for 10:00:00 — advance to 5 seconds before
196
+ await expect(page.getByTestId('notification-bell')).not.toHaveAttribute('data-count');
197
+
198
+ // Let real time tick from this point
199
+ await page.clock.resume();
200
+
201
+ // The notification should appear within a few seconds
202
+ await expect(page.getByTestId('notification-bell')).toHaveAttribute('data-count', '1', {
203
+ timeout: 10000,
204
+ });
205
+ });
206
+ ```
207
+
208
+ **JavaScript**
209
+ ```javascript
210
+ const { test, expect } = require('@playwright/test');
211
+
212
+ test('notification appears after scheduled trigger', async ({ page }) => {
213
+ await page.clock.install({ time: new Date('2025-03-15T09:59:55Z') });
214
+ await page.goto('/dashboard');
215
+
216
+ await page.clock.resume();
217
+
218
+ await expect(page.getByTestId('notification-bell')).toHaveAttribute('data-count', '1', {
219
+ timeout: 10000,
220
+ });
221
+ });
222
+ ```
223
+
224
+ ### Testing Date-Dependent UI
225
+
226
+ **Use when**: Features that change based on the current date -- age verification, expiration warnings, seasonal content, date pickers.
227
+ **Avoid when**: The date is passed from the server and does not depend on client-side `Date`.
228
+
229
+ **TypeScript**
230
+ ```typescript
231
+ import { test, expect } from '@playwright/test';
232
+
233
+ test('age gate blocks users under 18', async ({ page }) => {
234
+ // User born on 2010-01-15 — under 18 as of 2025-06-01
235
+ await page.clock.install({ time: new Date('2025-06-01T12:00:00') });
236
+ await page.goto('/age-restricted');
237
+
238
+ await page.getByLabel('Date of birth').fill('2010-01-15');
239
+ await page.getByRole('button', { name: 'Verify age' }).click();
240
+
241
+ await expect(page.getByText('You must be 18 or older')).toBeVisible();
242
+ });
243
+
244
+ test('age gate allows users 18 and older', async ({ page }) => {
245
+ await page.clock.install({ time: new Date('2025-06-01T12:00:00') });
246
+ await page.goto('/age-restricted');
247
+
248
+ await page.getByLabel('Date of birth').fill('2005-01-15');
249
+ await page.getByRole('button', { name: 'Verify age' }).click();
250
+
251
+ await expect(page.getByText('Welcome')).toBeVisible();
252
+ });
253
+
254
+ test('trial expiration banner shows at correct times', async ({ page }) => {
255
+ // Day 1 of 14-day trial
256
+ await page.clock.install({ time: new Date('2025-03-01T12:00:00Z') });
257
+ await page.goto('/dashboard');
258
+ await expect(page.getByTestId('trial-banner')).toContainText('13 days remaining');
259
+
260
+ // Day 12 — warning state
261
+ await page.clock.setFixedTime(new Date('2025-03-12T12:00:00Z'));
262
+ await page.reload();
263
+ await expect(page.getByTestId('trial-banner')).toContainText('2 days remaining');
264
+ await expect(page.getByTestId('trial-banner')).toHaveCSS('background-color', /rgb\(255/); // Red/warning
265
+
266
+ // Day 15 — expired
267
+ await page.clock.setFixedTime(new Date('2025-03-15T12:00:00Z'));
268
+ await page.reload();
269
+ await expect(page.getByText('Your trial has expired')).toBeVisible();
270
+ await expect(page.getByRole('button', { name: 'Upgrade now' })).toBeVisible();
271
+ });
272
+
273
+ test('date picker defaults to current mocked date', async ({ page }) => {
274
+ await page.clock.install({ time: new Date('2025-07-04T12:00:00') });
275
+ await page.goto('/booking');
276
+
277
+ await page.getByLabel('Check-in date').click();
278
+
279
+ // Calendar should open to July 2025
280
+ await expect(page.getByText('July 2025')).toBeVisible();
281
+
282
+ // Today (July 4) should be highlighted
283
+ const today = page.locator('[aria-current="date"]');
284
+ await expect(today).toHaveText('4');
285
+ });
286
+ ```
287
+
288
+ **JavaScript**
289
+ ```javascript
290
+ const { test, expect } = require('@playwright/test');
291
+
292
+ test('age gate blocks users under 18', async ({ page }) => {
293
+ await page.clock.install({ time: new Date('2025-06-01T12:00:00') });
294
+ await page.goto('/age-restricted');
295
+
296
+ await page.getByLabel('Date of birth').fill('2010-01-15');
297
+ await page.getByRole('button', { name: 'Verify age' }).click();
298
+
299
+ await expect(page.getByText('You must be 18 or older')).toBeVisible();
300
+ });
301
+
302
+ test('trial expiration shows correct days remaining', async ({ page }) => {
303
+ await page.clock.install({ time: new Date('2025-03-01T12:00:00Z') });
304
+ await page.goto('/dashboard');
305
+ await expect(page.getByTestId('trial-banner')).toContainText('13 days remaining');
306
+
307
+ await page.clock.setFixedTime(new Date('2025-03-15T12:00:00Z'));
308
+ await page.reload();
309
+ await expect(page.getByText('Your trial has expired')).toBeVisible();
310
+ });
311
+ ```
312
+
313
+ ### Timezone-Dependent Features
314
+
315
+ **Use when**: Testing features that combine mocked time with specific timezones.
316
+ **Avoid when**: The feature uses only UTC and does not render local times.
317
+
318
+ **TypeScript**
319
+ ```typescript
320
+ import { test, expect } from '@playwright/test';
321
+
322
+ test('business hours banner shows open/closed status', async ({ browser }) => {
323
+ // Business hours: 9 AM - 5 PM Eastern
324
+ const context = await browser.newContext({ timezoneId: 'America/New_York' });
325
+ const page = await context.newPage();
326
+
327
+ // 10 AM Eastern — should show "Open"
328
+ await page.clock.install({ time: new Date('2025-03-15T14:00:00Z') }); // 10 AM ET
329
+ await page.goto('/contact');
330
+ await expect(page.getByTestId('business-hours')).toContainText('Open');
331
+
332
+ // 6 PM Eastern — should show "Closed"
333
+ await page.clock.setFixedTime(new Date('2025-03-15T22:00:00Z')); // 6 PM ET
334
+ await page.reload();
335
+ await expect(page.getByTestId('business-hours')).toContainText('Closed');
336
+
337
+ await context.close();
338
+ });
339
+
340
+ test('scheduled event shows correct local time', async ({ browser }) => {
341
+ // Event at 2025-03-20T18:00:00Z
342
+
343
+ // User in Tokyo (UTC+9)
344
+ const tokyoCtx = await browser.newContext({ timezoneId: 'Asia/Tokyo' });
345
+ const tokyoPage = await tokyoCtx.newPage();
346
+ await tokyoPage.clock.install({ time: new Date('2025-03-20T10:00:00Z') });
347
+ await tokyoPage.goto('/events/upcoming');
348
+ // 18:00 UTC = 03:00 AM next day in Tokyo (UTC+9)
349
+ await expect(tokyoPage.getByTestId('event-time')).toContainText('3:00 AM');
350
+ await tokyoCtx.close();
351
+
352
+ // User in London (UTC+0 in March, before DST)
353
+ const londonCtx = await browser.newContext({ timezoneId: 'Europe/London' });
354
+ const londonPage = await londonCtx.newPage();
355
+ await londonPage.clock.install({ time: new Date('2025-03-20T10:00:00Z') });
356
+ await londonPage.goto('/events/upcoming');
357
+ await expect(londonPage.getByTestId('event-time')).toContainText('6:00 PM');
358
+ await londonCtx.close();
359
+ });
360
+ ```
361
+
362
+ **JavaScript**
363
+ ```javascript
364
+ const { test, expect } = require('@playwright/test');
365
+
366
+ test('business hours banner shows open/closed status', async ({ browser }) => {
367
+ const context = await browser.newContext({ timezoneId: 'America/New_York' });
368
+ const page = await context.newPage();
369
+
370
+ await page.clock.install({ time: new Date('2025-03-15T14:00:00Z') });
371
+ await page.goto('/contact');
372
+ await expect(page.getByTestId('business-hours')).toContainText('Open');
373
+
374
+ await page.clock.setFixedTime(new Date('2025-03-15T22:00:00Z'));
375
+ await page.reload();
376
+ await expect(page.getByTestId('business-hours')).toContainText('Closed');
377
+
378
+ await context.close();
379
+ });
380
+ ```
381
+
382
+ ## Decision Guide
383
+
384
+ | Scenario | API | Why |
385
+ |---|---|---|
386
+ | Check UI at a specific date/time | `clock.install()` + `clock.setFixedTime()` | Time is frozen; no timer ticking |
387
+ | Test countdown or timer behavior | `clock.install()` + `clock.fastForward()` | Fires timers as time advances without real waiting |
388
+ | Test after a long idle period | `clock.install()` + `clock.fastForward('30:00')` | Simulates 30 minutes without waiting 30 minutes |
389
+ | Start mocked, then tick normally | `clock.install()` + `clock.resume()` | Useful when you need real `requestAnimationFrame` after setup |
390
+ | Different timezone display | `browser.newContext({ timezoneId })` | Affects `Date` timezone rendering |
391
+ | Timezone + mocked time | `newContext({ timezoneId })` + `clock.install()` | Both timezone and absolute time are controlled |
392
+ | Test date picker defaults | `clock.install()` with target date | Calendar opens to the mocked "today" |
393
+ | Test DST transitions | Set `timezoneId` + `install` at DST boundary | Tests the most common timezone bugs |
394
+
395
+ ## Anti-Patterns
396
+
397
+ | Don't Do This | Problem | Do This Instead |
398
+ |---|---|---|
399
+ | Calling `clock.install()` after `page.goto()` | Page already loaded with real `Date`; timers already fired | Call `clock.install()` BEFORE `page.goto()` |
400
+ | Using `page.waitForTimeout(30000)` to test a 30-second timer | Wastes 30 real seconds per test run | `page.clock.fastForward('00:30')` completes instantly |
401
+ | Testing time without mocking the clock | Results depend on when the test runs (morning vs evening, Monday vs Sunday) | Always mock time for time-dependent assertions |
402
+ | Using `setFixedTime` when you need timers to fire | `setFixedTime` freezes time; `setInterval`/`setTimeout` will not trigger | Use `fastForward` to advance time and fire pending timers |
403
+ | Mocking only `Date.now()` via `page.evaluate` | Does not affect `setTimeout`, `setInterval`, or `requestAnimationFrame` | Use `page.clock.install()` which mocks all time APIs |
404
+ | Forgetting timezone when testing dates | Test passes locally but fails in CI (different timezone) | Always set `timezoneId` in context or use UTC dates |
405
+ | Advancing time in very small increments | Slow test; many unnecessary timer firings | Advance to the exact time of interest in one call |
406
+ | Not calling `resume()` before real-time-dependent assertions | Mocked timers will not fire naturally; assertions time out | Call `clock.resume()` when you need real time to flow |
407
+
408
+ ## Troubleshooting
409
+
410
+ | Symptom | Likely Cause | Fix |
411
+ |---|---|---|
412
+ | `clock.install()` has no effect | Called after `page.goto()`; page already has real `Date` | Move `clock.install()` before navigation |
413
+ | Timer callbacks never fire | Time is frozen with `setFixedTime`; timers need advancing | Use `fastForward()` to advance past the timer delay |
414
+ | `fastForward` does not trigger timer | Timer was registered with a delay longer than the fast-forward amount | Fast-forward by at least the timer's delay |
415
+ | Date is correct but timezone display is wrong | `clock.install` sets time in UTC; `timezoneId` not set on context | Create context with `{ timezoneId: 'Your/Timezone' }` |
416
+ | Animations break with mocked clock | `requestAnimationFrame` is mocked and does not fire naturally | Call `clock.resume()` before animation-dependent assertions |
417
+ | Test passes locally but fails in CI | Local timezone differs from CI timezone | Always set `timezoneId` explicitly in the context |
418
+ | `setFixedTime` throws "clock not installed" | `install()` was not called first | Call `page.clock.install()` before any other clock methods |
419
+ | Page makes fetch requests with wrong timestamps | Server sees mocked `Date.now()` in request payloads | This is expected; mock server responses if needed |
420
+
421
+ ## Related
422
+
423
+ - [core/i18n-and-localization.md](i18n-and-localization.md) -- timezone and locale testing patterns
424
+ - [core/assertions-and-waiting.md](assertions-and-waiting.md) -- auto-waiting vs time-based assertions
425
+ - [core/error-and-edge-cases.md](error-and-edge-cases.md) -- testing timeout and expiration edge cases
426
+ - [core/performance-testing.md](performance-testing.md) -- timing-related performance measurement
427
+ - [core/websockets-and-realtime.md](websockets-and-realtime.md) -- real-time features that depend on timing