Skip to content

Commit 18b9db6

Browse files
committed
Fix it and test being able to be called recursively
1 parent 6e28860 commit 18b9db6

File tree

3 files changed

+78
-24
lines changed

3 files changed

+78
-24
lines changed

src/bundles/unittest/src/__tests__/index.ts

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { list } from 'js-slang/dist/stdlib/list';
22
import { beforeEach, describe, expect, test, vi } from 'vitest';
33
import * as asserts from '../asserts';
44
import * as testing from '../functions';
5+
import { UnitestBundleInternalError } from '../types';
56

67
vi.spyOn(performance, 'now').mockReturnValue(0);
78

@@ -10,13 +11,27 @@ describe('Test \'it\' and \'describe\'', () => {
1011
testing.suiteResults.splice(0);
1112
});
1213

14+
test('it and describe correctly set and resets the value of current test and suite', () => {
15+
expect(testing.currentTest).toBeNull();
16+
expect(testing.currentSuite).toBeNull();
17+
testing.describe('suite', () => {
18+
expect(testing.currentSuite).not.toBeNull();
19+
expect(testing.currentTest).toBeNull();
20+
testing.it('test', () => {
21+
expect(testing.currentTest).toEqual('test');
22+
});
23+
});
24+
25+
expect(testing.currentTest).toBeNull();
26+
});
27+
1328
test('it() throws an error when called without describe', () => {
14-
expect(() => testing.it('desc', () => {})).toThrowError('\'it\' must be called from within a test suite!');
29+
expect(() => testing.it('desc', () => {})).toThrowError('it must be called from within a test suite!');
1530
});
1631

17-
test('it() throws an error even after describe', () => {
32+
test('it() throws an error even if it is called after describe', () => {
1833
testing.describe('a test', () => {});
19-
expect(() => testing.it('desc', () => {})).toThrowError('\'it\' must be called from within a test suite!');
34+
expect(() => testing.it('desc', () => {})).toThrowError('it must be called from within a test suite!');
2035
});
2136

2237
test('it() works fine from within a describe block', () => {
@@ -97,6 +112,16 @@ describe('Test \'it\' and \'describe\'', () => {
97112
passed: true,
98113
});
99114
});
115+
116+
test('it() throws when called within another it block', () => {
117+
const f = () => testing.describe('suite', () => {
118+
testing.it('test0', () => {
119+
testing.it('test1', () => {});
120+
});
121+
});
122+
123+
expect(f).toThrowError('it cannot be called from within another test!');
124+
});
100125
});
101126

102127
test('assert works', () => {
@@ -115,3 +140,9 @@ test('assert_contains works', () => {
115140
expect(() => asserts.assert_contains(list1, 2)).not.toThrow();
116141
expect(() => asserts.assert_contains(list1, 10)).toThrow();
117142
});
143+
144+
test('internal errors are not handled', () => {
145+
expect(() => {
146+
throw new UnitestBundleInternalError();
147+
}).toThrow();
148+
});

src/bundles/unittest/src/functions.ts

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
import context from 'js-slang/context';
22

3-
import type { Suite, SuiteResult, Test, TestResult } from './types';
3+
import {
4+
UnitestBundleInternalError,
5+
type Suite,
6+
type SuiteResult,
7+
type Test,
8+
type TestResult,
9+
type TestSuite
10+
} from './types';
411

512
function getNewSuite(name?: string): Suite {
613
return {
@@ -14,7 +21,9 @@ function getNewSuite(name?: string): Suite {
1421
* to collect those Suite Results since none of them will have a parent suite
1522
*/
1623
export const suiteResults: SuiteResult[] = [];
17-
let currentSuite: Suite | null = null;
24+
25+
export let currentSuite: Suite | null = null;
26+
export let currentTest: string | null = null;
1827

1928
function handleErr(err: any) {
2029
if (err.error && err.error.message) {
@@ -26,42 +35,54 @@ function handleErr(err: any) {
2635
throw err;
2736
}
2837

29-
/**
30-
* Defines a single test.
31-
* @param name Description for this test.
32-
* @param func Function containing assertions.
33-
*/
34-
export function it(name: string, func: Test): void {
38+
function runTest(name: string, funcName: string, func: Test) {
3539
if (currentSuite === null) {
36-
throw new Error(`'${it.name}' must be called from within a test suite!`);
40+
throw new UnitestBundleInternalError(`${funcName} must be called from within a test suite!`);
41+
}
42+
43+
if (currentTest !== null) {
44+
throw new UnitestBundleInternalError(`${funcName} cannot be called from within another test!`);
3745
}
3846

3947
try {
48+
currentTest = name;
4049
func();
4150
currentSuite.results.push({
4251
name,
4352
passed: true,
4453
});
4554
} catch (err) {
55+
if (err instanceof UnitestBundleInternalError) {
56+
throw err;
57+
}
58+
4659
const error = handleErr(err);
4760
currentSuite.results.push({
4861
name,
4962
passed: false,
5063
error,
5164
});
65+
} finally {
66+
currentTest = null;
5267
}
5368
}
5469

70+
/**
71+
* Defines a single test.
72+
* @param name Description for this test.
73+
* @param func Function containing assertions.
74+
*/
75+
export function it(name: string, func: Test): void {
76+
runTest(name, it.name, func);
77+
}
78+
5579
/**
5680
* Defines a single test.
5781
* @param msg Description for this test.
5882
* @param func Function containing assertions.
5983
*/
6084
export function test(msg: string, func: Test): void {
61-
if (currentSuite === null) {
62-
throw new Error(`${test.name} must be called from within a test suite!`);
63-
}
64-
it(msg, func);
85+
runTest(msg, test.name, func);
6586
}
6687

6788
function determinePassCount(results: (TestResult | SuiteResult)[]): number {
@@ -82,7 +103,7 @@ function determinePassCount(results: (TestResult | SuiteResult)[]): number {
82103
* @param msg Description for this test suite.
83104
* @param func Function containing tests.
84105
*/
85-
export function describe(msg: string, func: Test): void {
106+
export function describe(msg: string, func: TestSuite): void {
86107
const oldSuite = currentSuite;
87108
const newSuite = getNewSuite(msg);
88109

src/bundles/unittest/src/types.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
export type ErrorLogger = (
2-
error: string[] | string,
3-
isSlangError?: boolean
4-
) => void;
5-
61
/**
72
* Represents a function that when called, should either execute successfully
83
* or throw an assertion error
@@ -42,12 +37,12 @@ export interface SuiteResult {
4237
passed: boolean;
4338
}
4439

45-
export type TestSuccess = {
40+
export interface TestSuccess {
4641
passed: true;
4742
name: string;
4843
};
4944

50-
export type TestFailure = {
45+
export interface TestFailure {
5146
passed: false;
5247
name: string;
5348
error: string;
@@ -58,3 +53,10 @@ export type TestResult = TestSuccess | TestFailure;
5853
export interface UnittestModuleState {
5954
suiteResults: SuiteResult[];
6055
}
56+
57+
/**
58+
* These errors represent errors that shouldn't be handled as if they were thrown
59+
* by the assertion functions
60+
*/
61+
export class UnitestBundleInternalError extends Error {
62+
};

0 commit comments

Comments
 (0)