Skip to content

Commit

Permalink
Merge pull request #167 from OneBusAway/more-tests
Browse files Browse the repository at this point in the history
More tests
  • Loading branch information
aaronbrethorst authored Jan 17, 2025
2 parents 7bbfcc2 + 193a898 commit 35a7025
Show file tree
Hide file tree
Showing 5 changed files with 331 additions and 2 deletions.
2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ export default [
}
},
{
ignores: ['build/', '.svelte-kit/', 'dist/', 'src/lib/googleMaps.js']
ignores: ['build/', '.svelte-kit/', 'dist/', 'src/lib/googleMaps.js', 'coverage']
}
];
10 changes: 9 additions & 1 deletion src/lib/obaSdk.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,16 @@ const oba = new onebusaway({
});

export function handleOBAResponse(response, entityName) {
if (!response) {
throw error(500, `Unable to fetch ${entityName}.`);
}

if (typeof response.code === 'undefined') {
throw error(500, `Unable to fetch ${entityName}.`);
}

if (response.code !== 200) {
return error(500, `Unable to fetch ${entityName}.`);
throw error(500, `Unable to fetch ${entityName}.`);
}

return json(response);
Expand Down
140 changes: 140 additions & 0 deletions src/tests/lib/obaSdk.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import onebusaway from 'onebusaway-sdk';
import { handleOBAResponse } from '$lib/obaSdk';
import { error, json } from '@sveltejs/kit';

// Mock the onebusaway-sdk
vi.mock('onebusaway-sdk');

// Mock @sveltejs/kit error and json functions
vi.mock('@sveltejs/kit', () => ({
error: vi.fn((status, message) => {
throw new Error(message);
}),
json: vi.fn((data) => ({
status: 200,
body: data
}))
}));

// Mock environment variables
vi.mock('$env/static/public', () => ({
PUBLIC_OBA_SERVER_URL: 'https://test-api.example.com'
}));

vi.mock('$env/static/private', () => ({
PRIVATE_OBA_API_KEY: 'test-api-key'
}));

describe('OneBusAway Client', () => {
beforeEach(() => {
vi.clearAllMocks();
});

describe('OBA Client Initialization', () => {
it('should initialize with correct configuration', () => {
new onebusaway({
baseURL: 'https://test-api.example.com',
apiKey: 'test-api-key'
});

expect(onebusaway).toHaveBeenCalledWith({
baseURL: 'https://test-api.example.com',
apiKey: 'test-api-key'
});
});
});

describe('handleOBAResponse', () => {
it('should return JSON response when status code is 200', () => {
const mockResponse = {
code: 200,
data: {
stops: []
}
};

const result = handleOBAResponse(mockResponse, 'stops');
expect(json).toHaveBeenCalledWith(mockResponse);
expect(result.status).toBe(200);
expect(result.body).toEqual(mockResponse);
});

it('should throw error when status code is not 200', () => {
const mockResponse = {
code: 404,
data: null
};

expect(() => handleOBAResponse(mockResponse, 'stops')).toThrow(/Unable to fetch stops/);
expect(error).toHaveBeenCalledWith(500, 'Unable to fetch stops.');
});

it('should handle undefined response gracefully', () => {
expect(() => handleOBAResponse(undefined, 'stops')).toThrow(/Unable to fetch stops/);
expect(error).toHaveBeenCalledWith(500, 'Unable to fetch stops.');
});

it('should handle null response gracefully', () => {
expect(() => handleOBAResponse(null, 'stops')).toThrow(/Unable to fetch stops/);
expect(error).toHaveBeenCalledWith(500, 'Unable to fetch stops.');
});

it('should handle response with missing code gracefully', () => {
const mockResponse = {
data: {
stops: []
}
};

expect(() => handleOBAResponse(mockResponse, 'stops')).toThrow(/Unable to fetch stops/);
expect(error).toHaveBeenCalledWith(500, 'Unable to fetch stops.');
});
});
});

describe('OBA Client Integration', () => {
let oba;

beforeEach(() => {
vi.clearAllMocks();
oba = new onebusaway({
baseURL: 'https://test-api.example.com',
apiKey: 'test-api-key'
});
});

it('should handle successful API responses', async () => {
const mockApiResponse = {
code: 200,
data: {
stops: [{ id: 1, name: 'Test Stop' }]
}
};

// Mock a successful API call
oba.stops = vi.fn().mockResolvedValue(mockApiResponse);

const response = await oba.stops();
const result = handleOBAResponse(response, 'stops');

expect(json).toHaveBeenCalledWith(mockApiResponse);
expect(result.status).toBe(200);
expect(result.body).toEqual(mockApiResponse);
});

it('should handle failed API responses', async () => {
const mockApiResponse = {
code: 500,
data: null
};

// Mock a failed API call
oba.stops = vi.fn().mockResolvedValue(mockApiResponse);

const response = await oba.stops();

expect(() => handleOBAResponse(response, 'stops')).toThrow(/Unable to fetch stops/);
expect(error).toHaveBeenCalledWith(500, 'Unable to fetch stops.');
});
});
74 changes: 74 additions & 0 deletions src/tests/lib/urls.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { describe, it, expect } from 'vitest';
import { buildURL } from '$lib/urls';

describe('buildURL', () => {
it('should build a basic URL with query parameters', () => {
const result = buildURL('http://example.com', 'api/data', { key: 'value', page: '1' });
expect(result).toBe('http://example.com/api/data?key=value&page=1');
});

it('should handle trailing slashes in baseURL', () => {
const result = buildURL('http://example.com/', 'api/data', { key: 'value' });
expect(result).toBe('http://example.com/api/data?key=value');
});

it('should handle leading slashes in path', () => {
const result = buildURL('http://example.com', '/api/data', { key: 'value' });
expect(result).toBe('http://example.com/api/data?key=value');
});

it('should handle both trailing and leading slashes', () => {
const result = buildURL('http://example.com/', '/api/data', { key: 'value' });
expect(result).toBe('http://example.com/api/data?key=value');
});

it('should handle multiple trailing slashes in baseURL', () => {
const result = buildURL('http://example.com///', 'api/data', { key: 'value' });
expect(result).toBe('http://example.com/api/data?key=value');
});

it('should handle multiple leading slashes in path', () => {
const result = buildURL('http://example.com', '///api/data', { key: 'value' });
expect(result).toBe('http://example.com/api/data?key=value');
});

it('should handle empty query parameters', () => {
const result = buildURL('http://example.com', 'api/data', {});
expect(result).toBe('http://example.com/api/data?');
});

it('should encode query parameter values', () => {
const result = buildURL('http://example.com', 'api/data', {
key: 'value with spaces',
special: '!@#$%'
});
expect(result).toBe(
'http://example.com/api/data?key=value+with+spaces&special=%21%40%23%24%25'
);
});

it('should convert undefined values to "undefined" string', () => {
const result = buildURL('http://example.com', 'api/data', { key: undefined, value: 'test' });
expect(result).toBe('http://example.com/api/data?key=undefined&value=test');
});

it('should convert null values to "null" string', () => {
const result = buildURL('http://example.com', 'api/data', { key: null, value: 'test' });
expect(result).toBe('http://example.com/api/data?key=null&value=test');
});

it('should convert array to comma-separated string', () => {
const result = buildURL('http://example.com', 'api/data', { items: ['a', 'b', 'c'] });
expect(result).toBe('http://example.com/api/data?items=a%2Cb%2Cc');
});

it('should handle boolean values', () => {
const result = buildURL('http://example.com', 'api/data', { isActive: true, isDeleted: false });
expect(result).toBe('http://example.com/api/data?isActive=true&isDeleted=false');
});

it('should handle number values', () => {
const result = buildURL('http://example.com', 'api/data', { count: 42, price: 19.99 });
expect(result).toBe('http://example.com/api/data?count=42&price=19.99');
});
});
107 changes: 107 additions & 0 deletions src/tests/lib/utils.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { describe, it, expect, vi, afterEach, beforeEach } from 'vitest';
import { debounce } from '$lib/utils';

describe('debounce', () => {
beforeEach(() => {
vi.useFakeTimers();
});

afterEach(() => {
vi.restoreAllMocks();
});

it('should call the function only once after the wait time', async () => {
const mockFn = vi.fn();
const debouncedFn = debounce(mockFn, 100);

debouncedFn();
expect(mockFn).not.toBeCalled();

vi.advanceTimersByTime(50);
expect(mockFn).not.toBeCalled();

vi.advanceTimersByTime(50);
expect(mockFn).toBeCalledTimes(1);
});

it('should reset the timer when called again before wait time', () => {
const mockFn = vi.fn();
const debouncedFn = debounce(mockFn, 100);

debouncedFn();
vi.advanceTimersByTime(50);
debouncedFn();
vi.advanceTimersByTime(50);
expect(mockFn).not.toBeCalled();

vi.advanceTimersByTime(50);
expect(mockFn).toBeCalledTimes(1);
});

it('should pass arguments to the debounced function', () => {
const mockFn = vi.fn();
const debouncedFn = debounce(mockFn, 100);

debouncedFn('test', 123);
vi.advanceTimersByTime(100);

expect(mockFn).toBeCalledWith('test', 123);
});

it('should pass the latest arguments when called multiple times', () => {
const mockFn = vi.fn();
const debouncedFn = debounce(mockFn, 100);

debouncedFn('first', 1);
vi.advanceTimersByTime(50);

debouncedFn('second', 2);
vi.advanceTimersByTime(100);

expect(mockFn).toBeCalledTimes(1);
expect(mockFn).toBeCalledWith('second', 2);
});

it('should maintain correct context (this binding)', () => {
const context = {
value: 'test',
method: vi.fn(function () {
return this.value;
})
};

const debouncedMethod = debounce(context.method, 100);
context.debouncedMethod = debouncedMethod;

context.debouncedMethod();
vi.advanceTimersByTime(100);

expect(context.method).toBeCalledTimes(1);
expect(context.method.mock.results[0].value).toBe('test');
});

it('should handle zero wait time', () => {
const mockFn = vi.fn();
const debouncedFn = debounce(mockFn, 0);

debouncedFn();
vi.advanceTimersByTime(0);

expect(mockFn).toBeCalledTimes(1);
});

it('should handle multiple rapid calls', () => {
const mockFn = vi.fn();
const debouncedFn = debounce(mockFn, 100);

debouncedFn();
debouncedFn();
debouncedFn();
debouncedFn();
debouncedFn();

vi.advanceTimersByTime(100);

expect(mockFn).toBeCalledTimes(1);
});
});

0 comments on commit 35a7025

Please sign in to comment.