7.5 KiB
7.5 KiB
Testing Patterns
Analysis Date: 2026-01-11
Test Framework
Runner:
- Playwright 1.40.0 - E2E testing for React app (
jam-ui/package.json,jam-ui/playwright.config.ts) - Jest - Unit testing for React (inferred from test file patterns)
- RSpec 2.11 - Ruby testing framework (
ruby/Gemfile) - Jasmine - Legacy JavaScript tests with Karma runner (
web/spec/javascripts/karma.conf.js)
Assertion Library:
- Jest
expect- Unit tests - Playwright assertions - E2E tests (
expect(page).toHaveText(...)) - RSpec expectations - Ruby tests (
.should,expect { }.to)
Run Commands:
# React tests
cd jam-ui
npm test # Playwright E2E tests
npx cypress run # Cypress tests (alternative)
# Ruby tests
cd ruby
bundle exec rspec # Ruby gem tests
cd web
bundle exec rspec # Rails backend tests
cd websocket-gateway
bundle exec rspec # WebSocket gateway tests
Test File Organization
Location:
- React unit tests:
jam-ui/tests/helpers/rest.test.js(co-located pattern) - Playwright E2E:
jam-ui/test/friends.spec.ts(TypeScript) - Legacy JavaScript:
web/spec/javascripts/*.spec.js - Ruby tests:
ruby/spec/jam_ruby/models/*.spec.rb,web/spec/controllers/*.spec.rb
Naming:
- Unit tests:
{module}.test.js(e.g.,rest.test.js) - E2E tests:
{feature}.spec.ts(e.g.,friends.spec.ts) - Ruby tests:
{class_name}_spec.rb(e.g.,text_message_spec.rb)
Structure:
jam-ui/
tests/helpers/rest.test.js # Jest unit tests
test/friends.spec.ts # Playwright E2E
web/
spec/
controllers/ # Controller specs
javascripts/ # Legacy Jasmine tests
karma.conf.js # Karma configuration
ruby/
spec/jam_ruby/models/ # Model specs
Test Structure
Jest Unit Tests:
// jam-ui/tests/helpers/rest.test.js
describe('rest helpers', () => {
test('getPersonById calls correct URL', async () => {
const payload = { id: 42, name: 'John' };
mockResolved(payload);
const result = await rest.getPersonById(42);
expectCalledWith('/users/42/profile');
expect(result).toEqual(payload);
});
});
Playwright E2E Tests:
// jam-ui/test/friends.spec.ts
import { test, expect } from '@playwright/test';
test.describe.serial('Friends page', () => {
test.use({ storageState: 'test/storageState/user1.json' });
test('Homepage', async ({ page }) => {
await page.goto('/');
await expect(page.locator('h1').first()).toHaveText('Dashboard - Home');
});
});
RSpec Ruby Tests:
# ruby/spec/jam_ruby/models/text_message_spec.rb
describe TextMessage do
before do
TextMessage.delete_all
@target_user = FactoryGirl.create(:user)
@msg = TextMessage.new(:target_user_id => @target_user.id)
end
it "should retrieve conversation for both users" do
@msg.message = "Test message"
@msg.save!
messages = TextMessage.index(@target_user.id, @source_user.id)
messages.count.should == 1
end
end
Jasmine Legacy Tests:
// web/spec/javascripts/jamserver.spec.js
describe("JamServer", function() {
beforeEach(function() {
jamserver = context.JK.JamServer;
});
it("Subscribing to ping should call function", function() {
var called = false;
jamserver.registerMessageCallback(context.JK.MessageType.PING_ACK, function() {
called = true;
});
expect(called).toBeTruthy();
});
});
Patterns:
- Jest:
describe()blocks for grouping,test()orit()for individual tests - Playwright:
test.describe()for grouping,test()for individual E2E scenarios - RSpec:
describeblocks,beforehooks,itblocks with expectations - Jasmine:
describe()andit()blocks,beforeEach()hooks
Mocking
Framework:
- Jest built-in mocking:
jest.mock()for module mocking - RSpec: FactoryGirl for test data fixtures
- Manual mocks in test helpers
Jest Patterns:
// jam-ui/tests/helpers/rest.test.js
// Mock apiFetch module
const mockResolved = (payload) => {
apiFetch.mockResolvedValue(payload);
};
const expectCalledWith = (url) => {
expect(apiFetch).toHaveBeenCalledWith(url);
};
RSpec Patterns:
# ruby/spec/jam_ruby/models/text_message_spec.rb
before do
@target_user = FactoryGirl.create(:user)
@source_user = FactoryGirl.create(:user)
end
What to Mock:
- External API calls (via
apiFetch()in frontend) - Database queries (via FactoryGirl fixtures in Ruby)
- Time/dates (when testing time-sensitive logic)
- WebSocket connections (in unit tests)
What NOT to Mock:
- Pure functions and utilities
- Internal business logic
- Simple data transformations
Fixtures and Factories
Test Data:
- FactoryGirl for Ruby models:
FactoryGirl.create(:user)inruby/spec/ - Manual test helpers in JavaScript:
mockResolved(),expectCalledWith()injam-ui/tests/helpers/rest.test.js - Playwright storage state:
test.use({ storageState: 'test/storageState/user1.json' })for authenticated tests
Location:
- JavaScript helpers:
jam-ui/tests/helpers/(if applicable) - Ruby factories: Defined in
ruby/spec/orweb/spec/(FactoryGirl configuration) - Playwright fixtures:
jam-ui/test/storageState/(session cookies for auth)
Coverage
Requirements:
- No enforced coverage targets
- Coverage tracked for awareness, not enforcement
- Focus on critical paths (API endpoints, core models, business logic)
Configuration:
- Jest: Built-in coverage via
--coverageflag - RSpec: SimpleCov gem for Ruby code coverage
- Playwright: No coverage (E2E tests focus on integration, not coverage)
View Coverage:
# Jest coverage (if configured)
cd jam-ui
npm test -- --coverage
# RSpec coverage
cd ruby
bundle exec rspec
# Check coverage/index.html
Test Types
Unit Tests:
- Scope: Single function/module in isolation
- Mocking: Mock all external dependencies (API, database)
- Speed: Fast (<100ms per test)
- Examples:
jam-ui/tests/helpers/rest.test.js,ruby/spec/jam_ruby/models/*.spec.rb
Integration Tests:
- Scope: Multiple modules working together
- Mocking: Mock only external boundaries (network, file system)
- Examples: Rails controller specs testing controller + model interaction
E2E Tests:
- Framework: Playwright 1.40.0 (primary), Cypress (alternative mentioned in CLAUDE.md)
- Scope: Full user flows through UI
- Setup: Uses storage state for authenticated sessions
- Location:
jam-ui/test/*.spec.ts
Common Patterns
Async Testing:
// Jest async/await
test('handles async operation', async () => {
const result = await asyncFunction();
expect(result).toBe('expected');
});
// Playwright async
test('async page interaction', async ({ page }) => {
await page.goto('/');
await expect(page.locator('h1')).toHaveText('Title');
});
Error Testing:
// Jest error testing
test('throws on invalid input', () => {
expect(() => functionCall()).toThrow('error message');
});
// Async error
test('rejects on failure', async () => {
await expect(asyncCall()).rejects.toThrow('error message');
});
Before/After Hooks:
- Jest:
beforeEach(),afterEach()for setup/teardown - RSpec:
before do ... end,after do ... end - Playwright:
test.beforeAll(),test.afterAll()for global setup
Snapshot Testing:
- Not used in this codebase
- Prefer explicit assertions for clarity
Testing analysis: 2026-01-11 Update when test patterns change