@@ -18,7 +18,7 @@ The project uses Vitest for unit testing with workspace-based configuration:
1818
1919```typescript
2020// vitest.config.mts
21- import { defineConfig } from 'vitest/config';
21+ import { defineConfig } from 'vitest/config'
2222
2323export default defineConfig({
2424 test: {
@@ -48,7 +48,7 @@ export default defineConfig({
4848 },
4949 ],
5050 },
51- });
51+ })
5252```
5353
5454### Playwright for E2E Tests
@@ -57,8 +57,8 @@ E2E tests use Playwright with the service auto-started:
5757
5858```typescript
5959// playwright.config.ts
60- import type { PlaywrightTestConfig } from '@playwright/test';
61- import { devices } from '@playwright/test';
60+ import type { PlaywrightTestConfig } from '@playwright/test'
61+ import { devices } from '@playwright/test'
6262
6363const config: PlaywrightTestConfig = {
6464 testDir: 'e2e',
@@ -76,7 +76,7 @@ const config: PlaywrightTestConfig = {
7676 { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
7777 { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
7878 ],
79- };
79+ }
8080```
8181
8282## Unit Testing with Vitest
@@ -98,97 +98,97 @@ service/src/
9898Use `describe`, `it`, and `expect` from Vitest:
9999
100100```typescript
101- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
101+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
102102
103103describe('MyService', () => {
104104 describe('methodName', () => {
105105 it('should do something when condition', () => {
106106 // Arrange
107- const input = 'test';
108-
107+ const input = 'test'
108+
109109 // Act
110- const result = myFunction(input);
111-
110+ const result = myFunction(input)
111+
112112 // Assert
113- expect(result).toBe('expected');
114- });
115- });
116- });
113+ expect(result).toBe('expected')
114+ })
115+ })
116+ })
117117```
118118
119119### Testing Services
120120
121121Test service methods with mocked dependencies:
122122
123123```typescript
124- import { describe, it, expect, vi, beforeEach } from 'vitest';
125- import { Injector } from '@furystack/inject';
124+ import { describe, it, expect, vi, beforeEach } from 'vitest'
125+ import { Injector } from '@furystack/inject'
126126
127127describe('SessionService', () => {
128- let injector: Injector;
129- let sessionService: SessionService;
130-
128+ let injector: Injector
129+ let sessionService: SessionService
130+
131131 beforeEach(() => {
132- injector = new Injector();
132+ injector = new Injector()
133133 // Set up mocks
134134 const mockApiClient = {
135135 call: vi.fn(),
136- };
137- injector.setExplicitInstance(BoilerplateApiClient , mockApiClient);
138- sessionService = injector.getInstance(SessionService);
139- });
140-
136+ }
137+ injector.setExplicitInstance(StackCraftApiClient , mockApiClient)
138+ sessionService = injector.getInstance(SessionService)
139+ })
140+
141141 it('should initialize with unauthenticated state', async () => {
142- expect(sessionService.state.getValue()).toBe('initializing');
143- });
144- });
142+ expect(sessionService.state.getValue()).toBe('initializing')
143+ })
144+ })
145145```
146146
147147### Mocking with Vitest
148148
149149Use `vi.fn()` for function mocks and `vi.spyOn()` for spying:
150150
151151```typescript
152- import { vi } from 'vitest';
152+ import { vi } from 'vitest'
153153
154154// Mock a function
155- const mockFn = vi.fn().mockReturnValue('mocked');
155+ const mockFn = vi.fn().mockReturnValue('mocked')
156156
157157// Mock with implementation
158- const mockFn = vi.fn().mockImplementation((arg) => `result: ${arg}`);
158+ const mockFn = vi.fn().mockImplementation((arg) => `result: ${arg}`)
159159
160160// Mock async function
161- const mockAsync = vi.fn().mockResolvedValue({ data: 'test' });
161+ const mockAsync = vi.fn().mockResolvedValue({ data: 'test' })
162162
163163// Spy on method
164- const spy = vi.spyOn(service, 'method');
164+ const spy = vi.spyOn(service, 'method')
165165
166166// Verify calls
167- expect(mockFn).toHaveBeenCalled();
168- expect(mockFn).toHaveBeenCalledWith('arg');
169- expect(mockFn).toHaveBeenCalledTimes(2);
167+ expect(mockFn).toHaveBeenCalled()
168+ expect(mockFn).toHaveBeenCalledWith('arg')
169+ expect(mockFn).toHaveBeenCalledTimes(2)
170170```
171171
172172### Testing Observable Values
173173
174174Test ObservableValue subscriptions:
175175
176176```typescript
177- import { ObservableValue } from '@furystack/utils';
177+ import { ObservableValue } from '@furystack/utils'
178178
179179describe('ObservableValue', () => {
180180 it('should notify subscribers on value change', () => {
181- const observable = new ObservableValue<string>('initial');
182- const values: string[] = [];
183-
184- const subscription = observable.subscribe((value) => values.push(value));
185- observable.setValue('updated');
186-
187- expect(values).toEqual(['initial', 'updated']);
188-
189- subscription.dispose();
190- });
191- });
181+ const observable = new ObservableValue<string>('initial')
182+ const values: string[] = []
183+
184+ const subscription = observable.subscribe((value) => values.push(value))
185+ observable.setValue('updated')
186+
187+ expect(values).toEqual(['initial', 'updated'])
188+
189+ subscription.dispose()
190+ })
191+ })
192192```
193193
194194## E2E Testing with Playwright
@@ -209,26 +209,26 @@ e2e/
209209Use Playwright's test API:
210210
211211```typescript
212- import { expect, test } from '@playwright/test';
212+ import { expect, test } from '@playwright/test'
213213
214214test.describe('Feature Name', () => {
215215 test('should do something', async ({ page }) => {
216216 // Navigate
217- await page.goto('/');
218-
217+ await page.goto('/')
218+
219219 // Find elements
220- const element = page.locator('selector');
221-
220+ const element = page.locator('selector')
221+
222222 // Assert visibility
223- await expect(element).toBeVisible();
224-
223+ await expect(element).toBeVisible()
224+
225225 // Interact
226- await element.click();
227-
226+ await element.click()
227+
228228 // Assert result
229- await expect(page.locator('.result')).toHaveText('Expected');
230- });
231- });
229+ await expect(page.locator('.result')).toHaveText('Expected')
230+ })
231+ })
232232```
233233
234234### Locating Shades Components
@@ -238,58 +238,58 @@ Use shadow DOM component names as selectors:
238238```typescript
239239test('should interact with Shades components', async ({ page }) => {
240240 // Locate by shadow DOM name
241- const loginForm = page.locator('shade-login form');
242- await expect(loginForm).toBeVisible();
243-
241+ const loginForm = page.locator('shade-login form')
242+ await expect(loginForm).toBeVisible()
243+
244244 // Locate inputs within components
245- const usernameInput = loginForm.locator('input[name="userName"]');
246- const passwordInput = loginForm.locator('input[name="password"]');
247-
245+ const usernameInput = loginForm.locator('input[name="userName"]')
246+ const passwordInput = loginForm.locator('input[name="password"]')
247+
248248 // Fill inputs
249- await usernameInput.type('testuser');
250- await passwordInput.type('password');
251-
249+ await usernameInput.type('testuser')
250+ await passwordInput.type('password')
251+
252252 // Click buttons
253- const submitButton = page.locator('button', { hasText: 'Login' });
254- await submitButton.click();
255- });
253+ const submitButton = page.locator('button', { hasText: 'Login' })
254+ await submitButton.click()
255+ })
256256```
257257
258258### Authentication Flow Test
259259
260260Example of testing login/logout:
261261
262262```typescript
263- import { expect, test } from '@playwright/test';
263+ import { expect, test } from '@playwright/test'
264264
265265test.describe('Authentication', () => {
266266 test('Login and logout roundtrip', async ({ page }) => {
267- await page.goto('/');
268-
267+ await page.goto('/')
268+
269269 // Wait for login form
270- const loginForm = page.locator('shade-login form');
271- await expect(loginForm).toBeVisible();
272-
270+ const loginForm = page.locator('shade-login form')
271+ await expect(loginForm).toBeVisible()
272+
273273 // Fill credentials
274- await loginForm.locator('input[name="userName"]').type('testuser');
275- await loginForm.locator('input[name="password"]').type('password');
276-
274+ await loginForm.locator('input[name="userName"]').type('testuser')
275+ await loginForm.locator('input[name="password"]').type('password')
276+
277277 // Submit
278- await page.locator('button', { hasText: 'Login' }).click();
279-
278+ await page.locator('button', { hasText: 'Login' }).click()
279+
280280 // Verify logged in state
281- const welcomeTitle = page.locator('hello-world div h2');
282- await expect(welcomeTitle).toBeVisible();
283- await expect(welcomeTitle).toHaveText('Hello, testuser !');
284-
281+ const welcomeTitle = page.locator('hello-world div h2')
282+ await expect(welcomeTitle).toBeVisible()
283+ await expect(welcomeTitle).toHaveText('Hello, testuser !')
284+
285285 // Logout
286- const logoutButton = page.locator('shade-app-bar button >> text="Log Out"');
287- await logoutButton.click();
288-
286+ const logoutButton = page.locator('shade-app-bar button >> text="Log Out"')
287+ await logoutButton.click()
288+
289289 // Verify logged out
290- await expect(page.locator('shade-login form')).toBeVisible();
291- });
292- });
290+ await expect(page.locator('shade-login form')).toBeVisible()
291+ })
292+ })
293293```
294294
295295### Waiting for Elements
@@ -298,14 +298,14 @@ Use Playwright's auto-waiting or explicit waits:
298298
299299```typescript
300300// Auto-wait (recommended)
301- await expect(element).toBeVisible();
301+ await expect(element).toBeVisible()
302302
303303// Explicit wait
304- await page.waitForSelector('selector');
305- await page.waitForLoadState('networkidle');
304+ await page.waitForSelector('selector')
305+ await page.waitForLoadState('networkidle')
306306
307307// Wait for response
308- await page.waitForResponse('**/api/endpoint');
308+ await page.waitForResponse('**/api/endpoint')
309309```
310310
311311## Running Tests
0 commit comments