-- ROBLOX FIXME: add type constraint <EachCallback extends TestCallback>

type Each<EachCallback> =
	((table: EachTable, ...any) -> ((title: string, test: EachTestFn<EachCallback>, timeout: number?) -> ()))
	| (() -> () -> ())

export type HookBase = (testName: TestName, fn: TestFn, timeout: number?) -> ()

export type ItBase = typeof(setmetatable({} :: { each: Each<TestFn> }, {
	__call = function(_, testName: TestName, fn: TestFn, timeout: number?): () end,
}))

export type It = ItBase & { only: ItBase, skip: ItBase, todo: (testName: TestName) -> () }

export type ItConcurrentBase = typeof(setmetatable({} :: { each: Each<ConcurrentTestFn> }, {
	__call = function(_, testName: string, testFn: ConcurrentTestFn, timeout: number?): () end,
}))

export type ItConcurrentExtended = ItConcurrentBase & { only: ItConcurrentBase, skip: ItConcurrentBase }

export type ItConcurrent = It & { concurrent: ItConcurrentExtended }

export type DescribeBase = typeof(setmetatable({} :: { each: Each<BlockFn> }, {
	__call = function(_, blockName: BlockName, blockFn: BlockFn): () end,
}))

export type Describe = DescribeBase & { only: DescribeBase, skip: DescribeBase }

export type TestFrameworkGlobals = {
	it: ItConcurrent,
	test: ItConcurrent,
	fit: ItBase & { concurrent: ItConcurrentBase? },
	xit: ItBase,
	xtest: ItBase,
	describe: Describe,
	xdescribe: DescribeBase,
	fdescribe: DescribeBase,
	beforeAll: HookBase,
	beforeEach: HookBase,
	afterEach: HookBase,
	afterAll: HookBase,
}

export type GlobalAdditions = TestFrameworkGlobals & {
	__coverage__: CoverageMapData,
	jasmine: Jasmine,
	fail: () -> (),
	pending: () -> (),
	spyOn: () -> (),
	spyOnProperty: () -> (),
}
