Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .cursor/agents/reviewer-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ You are a unit test and coverage reviewer for code reviews.

## When Invoked

**IMPORTANT:** Run `yarn test:unit` exactly ONCE. Do NOT re-run the command for any reason (verification, double-checking, etc.). Base your entire report on the single execution.
**IMPORTANT:** Run `yarn test` exactly ONCE. Do NOT re-run the command for any reason (verification, double-checking, etc.). Base your entire report on the single execution.

### Step 1: Run Unit Tests

1. Run `yarn test:unit` once to execute all unit tests
1. Run `yarn test` once to execute all unit tests
2. Analyze the exit code and output from that single run
3. If any tests fail, report them as **Critical Issues**

Expand Down
2 changes: 1 addition & 1 deletion .cursor/rules/CODE_STYLE.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -317,5 +317,5 @@ return <div>Content</div>
- Prettier: `yarn format`
- ESLint: `yarn lint`
- Type Check: `yarn build`
- Unit Tests: `yarn test:unit`
- Unit Tests: `yarn test`
- E2E Tests: `yarn test:e2e`
140 changes: 70 additions & 70 deletions .cursor/rules/TESTING_GUIDELINES.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ describe('EnvironmentOptions', () => {
describe('getEnvironmentOptions', () => {
it('should return default options when no URL params', () => {
// Arrange
const url = new URL('http://localhost');
const url = new URL('http://localhost')

// Act
const result = getEnvironmentOptions(url);
const result = getEnvironmentOptions(url)

// Assert
expect(result.theme).toBe('dark');
});
});
});
expect(result.theme).toBe('dark')
})
})
})
```

### Test All Exported Functions
Expand All @@ -40,22 +40,22 @@ Every exported function should have tests:
// ✅ Good - testing public API
describe('orderFields', () => {
it('should order object fields alphabetically', () => {
const input = { z: 1, a: 2, m: 3 };
const result = orderFields(input);
expect(Object.keys(result)).toEqual(['a', 'm', 'z']);
});
const input = { z: 1, a: 2, m: 3 }
const result = orderFields(input)
expect(Object.keys(result)).toEqual(['a', 'm', 'z'])
})

it('should handle nested objects', () => {
const input = { outer: { z: 1, a: 2 } };
const result = orderFields(input);
expect(Object.keys(result.outer)).toEqual(['a', 'z']);
});
const input = { outer: { z: 1, a: 2 } }
const result = orderFields(input)
expect(Object.keys(result.outer)).toEqual(['a', 'z'])
})

it('should handle empty objects', () => {
const result = orderFields({});
expect(result).toEqual({});
});
});
const result = orderFields({})
expect(result).toEqual({})
})
})
```

### Test Edge Cases
Expand All @@ -66,25 +66,25 @@ Test boundary conditions and error scenarios:
// ✅ Good - testing edge cases
describe('parseJson', () => {
it('should handle valid JSON', () => {
const result = parseJson('{"key": "value"}');
expect(result).toEqual({ key: 'value' });
});
const result = parseJson('{"key": "value"}')
expect(result).toEqual({ key: 'value' })
})

it('should handle invalid JSON', () => {
const result = parseJson('invalid');
expect(result).toBeNull();
});
const result = parseJson('invalid')
expect(result).toBeNull()
})

it('should handle empty string', () => {
const result = parseJson('');
expect(result).toBeNull();
});
const result = parseJson('')
expect(result).toBeNull()
})

it('should handle null input', () => {
const result = parseJson(null as unknown as string);
expect(result).toBeNull();
});
});
const result = parseJson(null as unknown as string)
expect(result).toBeNull()
})
})
```

### Avoid False Positive Tests
Expand All @@ -95,26 +95,26 @@ Ensure assertions always run:
// ❌ False positive risk - passes if function doesn't throw
it('should throw on error', async () => {
try {
await functionThatShouldThrow();
await functionThatShouldThrow()
} catch (error) {
expect(error).toBeInstanceOf(Error);
expect(error).toBeInstanceOf(Error)
}
});
})

// ✅ Correct - fails if assertions don't run
it('should throw on error', async () => {
expect.assertions(1);
expect.assertions(1)
try {
await functionThatShouldThrow();
await functionThatShouldThrow()
} catch (error) {
expect(error).toBeInstanceOf(Error);
expect(error).toBeInstanceOf(Error)
}
});
})

// ✅ Better - use expect().rejects
it('should throw on error', async () => {
await expect(functionThatShouldThrow()).rejects.toThrow();
});
await expect(functionThatShouldThrow()).rejects.toThrow()
})
```

## E2E Testing with Playwright
Expand All @@ -123,58 +123,58 @@ it('should throw on error', async () => {

```typescript
// e2e/page.spec.ts
import { test, expect } from '@playwright/test';
import { test, expect } from '@playwright/test'

test.describe('Home Page', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
});
await page.goto('/')
})

test('should display editor', async ({ page }) => {
await expect(page.locator('.monaco-editor')).toBeVisible();
});
await expect(page.locator('.monaco-editor')).toBeVisible()
})

test('should format JSON on button click', async ({ page }) => {
const editor = page.locator('.monaco-editor');
await editor.fill('{"z":1,"a":2}');
await page.click('button:has-text("Format")');
const editor = page.locator('.monaco-editor')
await editor.fill('{"z":1,"a":2}')

await page.click('button:has-text("Format")')

// Verify formatted output
await expect(editor).toContainText('"a": 2');
});
});
await expect(editor).toContainText('"a": 2')
})
})
```

### Visual Regression Testing

```typescript
// ✅ Good - visual snapshot testing
test('should match visual snapshot', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveScreenshot('home-content.png');
});
await page.goto('/')
await expect(page).toHaveScreenshot('home-content.png')
})

test('should match title snapshot', async ({ page }) => {
await page.goto('/');
const title = page.locator('h1');
await expect(title).toHaveScreenshot('title.png');
});
await page.goto('/')
const title = page.locator('h1')
await expect(title).toHaveScreenshot('title.png')
})
```

### Waiting for Elements

```typescript
// ✅ Good - wait for element to be ready
test('should load editor', async ({ page }) => {
await page.goto('/');
await page.goto('/')

// Wait for Monaco editor to initialize
await page.waitForSelector('.monaco-editor', { state: 'visible' });
await page.waitForSelector('.monaco-editor', { state: 'visible' })

// Now interact with the editor
await page.click('.monaco-editor');
});
await page.click('.monaco-editor')
})
```

## Test Coverage
Expand All @@ -187,7 +187,7 @@ test('should load editor', async ({ page }) => {

```bash
# Run unit tests with coverage
yarn test:unit --coverage
yarn test --coverage

# Run E2E tests
yarn test:e2e
Expand All @@ -199,13 +199,13 @@ yarn test:e2e

```bash
# Unit tests
yarn test:unit
yarn test

# E2E tests
yarn test:e2e

# All tests
yarn test:unit && yarn test:e2e
yarn test && yarn test:e2e
```

## Summary
Expand All @@ -231,4 +231,4 @@ yarn test:unit && yarn test:e2e

- Unit Tests: `vitest`
- E2E Tests: `playwright`
- Commands: `yarn test:unit`, `yarn test:e2e`
- Commands: `yarn test`, `yarn test:e2e`
2 changes: 1 addition & 1 deletion .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
- name: Test
run: yarn test:unit
run: yarn test
- uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
run: yarn lint

- name: Test
run: yarn test:unit
run: yarn test

- name: Apply release changes
run: yarn applyReleaseChanges
Expand Down
54 changes: 54 additions & 0 deletions .yarn/changelogs/frontend.923f81dd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<!-- version-type: patch -->
# frontend

<!--
FORMATTING GUIDE:

### Detailed Entry (appears first when merging)

Use h3 (###) and below for detailed entries with paragraphs, code examples, and lists.

### Simple List Items

- Simple changes can be added as list items
- They are collected together at the bottom of each section

TIP: When multiple changelog drafts are merged, heading-based entries
appear before simple list items within each section.
-->

## ✨ Features
<!-- PLACEHOLDER: Describe your shiny new features (feat:) -->

## 🐛 Bug Fixes
<!-- PLACEHOLDER: Describe the nasty little bugs that has been eradicated (fix:) -->

## 📚 Documentation
<!-- PLACEHOLDER: Describe documentation changes (docs:) -->

## ⚡ Performance
<!-- PLACEHOLDER: Describe performance improvements (perf:) -->

## ♻️ Refactoring

- Migrated inline styles to `css` property in Shades components (`Header`, `Layout`, `JsonSchemaSelector`, `ShareButton`, `ThemeSwitch`, `ComparePage`, `ValidatePage`) - leveraging the new CSS-in-JS API for cleaner styling with pseudo-class support

## 🧪 Tests
<!-- PLACEHOLDER: Describe test changes (test:) -->

## 📦 Build
<!-- PLACEHOLDER: Describe build system changes (build:) -->

## 👷 CI
<!-- PLACEHOLDER: Describe CI configuration changes (ci:) -->

## ⬆️ Dependencies

- Updated `@furystack/shades-common-components` from 10.x to 11.x (with new CSS-in-JS API)
- Updated `@furystack/shades` from 11.0.x to 11.1.x
- Updated other FuryStack packages (`core`, `inject`, `logging`, `rest-client-fetch`, `utils`)
- Updated `monaco-editor` from 0.54.0 to 0.55.1
- Updated `vite` from 7.1.x to 7.3.x and `vitest` from 3.x to 4.x

## 🔧 Chores
<!-- PLACEHOLDER: Describe other changes (chore:) -->
Loading
Loading