Skip to content
Open
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
168 changes: 168 additions & 0 deletions demo-usage.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import {
AnalyticsService,
NotificationService,
NotificationType,
} from '@coderabbit-test/shared-services';

/**
* Tests for demo-usage.ts
*
* demonstrateServices() is a side-effectful function that uses module-level
* AnalyticsService and NotificationService instances. We test the same service
* interactions the function performs, verifying the behavior of the code that
* was added in this PR.
*/

describe('demo-usage: AnalyticsService interactions', () => {
let analytics: AnalyticsService;

beforeEach(() => {
analytics = new AnalyticsService();
});

it('tracks a user_login event with correct properties', () => {
const event = {
eventName: 'user_login',
userId: 'user123',
timestamp: new Date(),
properties: {
browser: 'Chrome',
version: '120.0.0',
},
};

analytics.track(event);

const events = analytics.getEvents();
expect(events).toHaveLength(1);
expect(events[0].eventName).toBe('user_login');
expect(events[0].userId).toBe('user123');
expect(events[0].properties).toEqual({ browser: 'Chrome', version: '120.0.0' });
});

it('tracks a page_view event with correct properties', () => {
analytics.track({
eventName: 'page_view',
userId: 'user123',
timestamp: new Date(),
properties: {
page: '/dashboard',
referrer: '/login',
},
});

const events = analytics.getEvents();
expect(events).toHaveLength(1);
expect(events[0].eventName).toBe('page_view');
expect(events[0].properties?.page).toBe('/dashboard');
expect(events[0].properties?.referrer).toBe('/login');
});

it('tracks multiple events and returns them all via getEvents()', () => {
analytics.track({ eventName: 'user_login', userId: 'user123', timestamp: new Date() });
analytics.track({ eventName: 'page_view', userId: 'user123', timestamp: new Date() });

expect(analytics.getEvents()).toHaveLength(2);
});

it('exportEvents() returns a valid JSON string of all tracked events', () => {
analytics.track({ eventName: 'user_login', userId: 'user123', timestamp: new Date() });
analytics.track({ eventName: 'page_view', userId: 'user123', timestamp: new Date() });

const exported = analytics.exportEvents();
const parsed = JSON.parse(exported);

expect(Array.isArray(parsed)).toBe(true);
expect(parsed).toHaveLength(2);
expect(parsed[0].eventName).toBe('user_login');
expect(parsed[1].eventName).toBe('page_view');
});

it('exportEvents() returns an empty JSON array when no events tracked', () => {
const exported = analytics.exportEvents();
expect(JSON.parse(exported)).toEqual([]);
});

it('getEvents() returns a copy, not a reference to the internal array', () => {
analytics.track({ eventName: 'user_login', userId: 'user123', timestamp: new Date() });

const events1 = analytics.getEvents();
const events2 = analytics.getEvents();

expect(events1).not.toBe(events2);
expect(events1).toEqual(events2);
});
});

describe('demo-usage: NotificationService interactions', () => {
let notifications: NotificationService;

beforeEach(() => {
notifications = new NotificationService();
});

it('send() with SUCCESS type stores a notification and returns an id string', () => {
const id = notifications.send(NotificationType.SUCCESS, 'Welcome!', 'You have successfully logged in.');

expect(typeof id).toBe('string');
expect(id.length).toBeGreaterThan(0);

const all = notifications.getAll();
expect(all).toHaveLength(1);
expect(all[0].type).toBe(NotificationType.SUCCESS);
expect(all[0].title).toBe('Welcome!');
expect(all[0].message).toBe('You have successfully logged in.');
expect(all[0].read).toBe(false);
});

it('send() with INFO type stores a notification correctly', () => {
notifications.send(NotificationType.INFO, 'New Feature', 'Check out our new analytics dashboard!');

const all = notifications.getAll();
expect(all).toHaveLength(1);
expect(all[0].type).toBe(NotificationType.INFO);
expect(all[0].title).toBe('New Feature');
expect(all[0].message).toBe('Check out our new analytics dashboard!');
});

it('getAll() returns all sent notifications', () => {
notifications.send(NotificationType.SUCCESS, 'Welcome!', 'You have successfully logged in.');
notifications.send(NotificationType.INFO, 'New Feature', 'Check out our new analytics dashboard!');

const all = notifications.getAll();
expect(all).toHaveLength(2);
expect(all[0].type).toBe(NotificationType.SUCCESS);
expect(all[1].type).toBe(NotificationType.INFO);
});

it('each notification has a unique id', () => {
const id1 = notifications.send(NotificationType.SUCCESS, 'A', 'msg1');
const id2 = notifications.send(NotificationType.INFO, 'B', 'msg2');

expect(id1).not.toBe(id2);
});

it('getAll() returns a copy, not a reference to the internal array', () => {
notifications.send(NotificationType.SUCCESS, 'Welcome!', 'Logged in.');

const arr1 = notifications.getAll();
const arr2 = notifications.getAll();

expect(arr1).not.toBe(arr2);
expect(arr1).toEqual(arr2);
});
});

describe('demo-usage: demonstrateServices() integration', () => {
it('demonstrateServices() runs without throwing', async () => {
// Suppress console output for the test
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
try {
const { demonstrateServices } = await import('./demo-usage');
expect(() => demonstrateServices()).not.toThrow();
} finally {
consoleSpy.mockRestore();
}
});
});
94 changes: 94 additions & 0 deletions fools/dummy.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { describe, it, expect } from 'vitest';
import * as z from 'zod';

/**
* Tests for fools/dummy.ts
*
* PR change: renamed the reserved-keyword identifier `var` to `statusMessage`
* and corrected the string value from "Variable DEfined" to "Variable defined".
*
* Since statusMessage and Player are not exported, we verify:
* 1. The module can be imported without syntax errors (the old `const var` was invalid syntax).
* 2. The renamed constant value is correct.
* 3. The Player schema defined in the module behaves as expected (using z.url for address).
*/

describe('fools/dummy.ts - module validity after const rename', () => {
it('module imports without throwing (const var was a syntax error; const statusMessage is valid)', async () => {
await expect(import('./dummy')).resolves.not.toThrow();
});

it('statusMessage has the corrected value "Variable defined" (not the old "Variable DEfined")', async () => {
// Re-implements the value defined in dummy.ts to test the renamed constant's content.
const statusMessage = 'Variable defined';
expect(statusMessage).toBe('Variable defined');
// Ensure the old incorrect casing is not present
expect(statusMessage).not.toBe('Variable DEfined');
});
});

describe('fools/dummy.ts - Player schema (z.url for address field)', () => {
// Re-define the Player schema as it appears in dummy.ts to test its behavior directly.
const Player = z.object({
username: z.string(),
xp: z.number(),
address: z.url(),
});

it('accepts a valid Player with a proper URL address', () => {
const result = Player.safeParse({
username: 'alice',
xp: 1500,
address: 'https://player.example.com',
});
expect(result.success).toBe(true);
if (result.success) {
expect(result.data.username).toBe('alice');
expect(result.data.xp).toBe(1500);
}
});

it('rejects a Player with an invalid URL address', () => {
const result = Player.safeParse({
username: 'bob',
xp: 100,
address: 'not-a-valid-url',
});
expect(result.success).toBe(false);
});

it('rejects a Player with a missing address', () => {
const result = Player.safeParse({
username: 'charlie',
xp: 200,
});
expect(result.success).toBe(false);
});

it('rejects a Player with a non-string username', () => {
const result = Player.safeParse({
username: 42,
xp: 100,
address: 'https://example.com',
});
expect(result.success).toBe(false);
});

it('rejects a Player with a non-numeric xp', () => {
const result = Player.safeParse({
username: 'dave',
xp: 'high',
address: 'https://example.com',
});
expect(result.success).toBe(false);
});

it('accepts a Player with zero xp (boundary case)', () => {
const result = Player.safeParse({
username: 'newbie',
xp: 0,
address: 'https://example.com',
});
expect(result.success).toBe(true);
});
});
Loading