Skip to content

Commit

Permalink
feat: support compiled tests with a sourcemap only (#49)
Browse files Browse the repository at this point in the history
  • Loading branch information
noomorph authored Aug 6, 2024
1 parent 2693295 commit 6d1ff05
Show file tree
Hide file tree
Showing 13 changed files with 151 additions and 29 deletions.
7 changes: 7 additions & 0 deletions e2e/tests/sanity/sourcemap.test.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions e2e/tests/sanity/sourcemap.test.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 3 additions & 27 deletions src/options/helpers/file-navigator/getFileNavigator.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,12 @@
import fs from 'node:fs/promises';
import path from 'node:path';

import type { KeyedHelperCustomizer } from 'jest-allure2-reporter';

import { FileNavigator } from '../../../utils';

class FileNavigatorCache {
#cache = new Map<string, Promise<FileNavigator | undefined>>();

resolve(filePath: string): Promise<FileNavigator | undefined> {
const absolutePath = path.resolve(filePath);
if (!this.#cache.has(absolutePath)) {
this.#cache.set(absolutePath, this.#createNavigator(absolutePath));
}

return this.#cache.get(absolutePath)!;
}

#createNavigator = async (filePath: string) => {
const sourceCode = await fs.readFile(filePath, 'utf8').catch(() => void 0);
return sourceCode == null ? undefined : new FileNavigator(sourceCode);
};

clear() {
this.#cache.clear();
}

static readonly instance = new FileNavigatorCache();
}
import { FileNavigatorCache } from '../../../utils';

export const getFileNavigator: KeyedHelperCustomizer<'getFileNavigator'> = () => {
const cache = new FileNavigatorCache();
const cache = FileNavigatorCache.instance;

return (maybeSegmentedFilePath) => {
const filePath = Array.isArray(maybeSegmentedFilePath)
? maybeSegmentedFilePath.join(path.sep)
Expand Down
3 changes: 2 additions & 1 deletion src/reporter/JestAllure2Reporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import type {

import { type ReporterConfig, resolveOptions } from '../options';
import { AllureMetadataProxy, MetadataSquasher } from '../metadata';
import { compactArray, stringifyValues } from '../utils';
import { compactArray, FileNavigatorCache, stringifyValues } from '../utils';
import { type AllureWriter, FileAllureWriter } from '../serialization';
import { log, optimizeForTracing } from '../logger';

Expand Down Expand Up @@ -155,6 +155,7 @@ export class JestAllure2Reporter extends JestMetadataReporter {
async onTestFileStart(test: Test) {
super.onTestFileStart(test);

await FileNavigatorCache.instance.scanSourcemap(test.path);
const execute = this.#onTestFileStart.bind(this, test);
const attempt = this.#attemptSync.bind(this, 'onTestFileStart()', execute);
const testPath = path.relative(this._globalConfig.rootDir, test.path);
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/modules/StepsDecorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export class StepsDecorator {

function resolveParameter(this: unknown[], parameter: UserParameter, index: number) {
const { name = `${index}`, ...options } =
typeof parameter === 'string' ? { name: parameter } : parameter ?? {};
typeof parameter === 'string' ? { name: parameter } : (parameter ?? {});

return [name, index, this[index], options, parameter] as ResolvedParameter;
}
Expand Down
43 changes: 43 additions & 0 deletions src/utils/FileNavigatorCache.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
jest.mock('../logger');

import path from 'node:path';

import { log } from '../logger';

import { FileNavigatorCache } from './FileNavigatorCache';

const __FIXTURES__ = path.join(__dirname, '__fixtures__');

describe('FileNavigatorCache', () => {
test('should handle non-existent sourcemap', async () => {
const cache = new FileNavigatorCache();
await cache.scanSourcemap(path.join(__FIXTURES__, 'non-existent'));
await expect(cache.resolve(path.join(__FIXTURES__, 'test.ts'))).resolves.toBeUndefined();
expect(log.error).not.toHaveBeenCalled();
});

test('should handle a broken sourcemap', async () => {
const cache = new FileNavigatorCache();
await cache.scanSourcemap(path.join(__FIXTURES__, 'broken'));
await expect(cache.resolve(path.join(__FIXTURES__, 'test.ts'))).resolves.toBeUndefined();
expect(log.error).toHaveBeenCalled();
});

test('should handle an empty sourcemap', async () => {
const cache = new FileNavigatorCache();
await cache.scanSourcemap(path.join(__FIXTURES__, 'empty'));
await expect(cache.resolve(path.join(__FIXTURES__, 'test.ts'))).resolves.toBeUndefined();
});

test('should handle a simple sourcemap', async () => {
const cache = new FileNavigatorCache();
await cache.scanSourcemap(path.join(__FIXTURES__, 'simple'));
await expect(cache.resolve(path.join(__FIXTURES__, 'test.ts'))).resolves.toBeDefined();
});

test('should handle a sourcemap with a sourceRoot', async () => {
const cache = new FileNavigatorCache();
await cache.scanSourcemap(path.join(__FIXTURES__, 'with-root'));
await expect(cache.resolve('/home/user/project/test.ts')).resolves.toBeDefined();
});
});
67 changes: 67 additions & 0 deletions src/utils/FileNavigatorCache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import path from 'node:path';
import fs from 'node:fs/promises';
import type { SourceMapPayload } from 'node:module';

import { log } from '../logger';

import { FileNavigator } from './index';

export class FileNavigatorCache {
#cache = new Map<string, Promise<FileNavigator | undefined>>();

resolve(filePath: string): Promise<FileNavigator | undefined> {
const absolutePath = path.isAbsolute(filePath) ? filePath : path.resolve(filePath);
if (!this.#cache.has(absolutePath)) {
this.#cache.set(absolutePath, this.#createNavigator(absolutePath));
}

return this.#cache.get(absolutePath)!;
}

async scanSourcemap(filePath: string): Promise<void> {
const sourceMapPath = `${filePath}.map`;

const doesNotExist = await fs.access(sourceMapPath).catch(() => true);
if (doesNotExist) return;

const sourceMapRaw = await fs.readFile(sourceMapPath, 'utf8').catch((error) => {
log.error(error, `Failed to read sourcemap for: ${filePath}`);
});
if (sourceMapRaw == null) return;

let sourceMap: SourceMapPayload | undefined;
try {
sourceMap = JSON.parse(sourceMapRaw);
} catch (error) {
log.error(error, `Failed to parse sourcemap for: ${filePath}`);
}
if (!sourceMap) return;

const { sourceRoot, sources, sourcesContent } = sourceMap;
if (!sources || !sourcesContent) return;

const baseDirectory =
sourceRoot && path.isAbsolute(sourceRoot) ? sourceRoot : path.dirname(filePath);
for (const [index, content] of sourcesContent.entries()) {
const source = sources[index];
if (!content || !source) continue;

const sourcePath = path.isAbsolute(source) ? source : path.resolve(baseDirectory, source);
if (this.#cache.has(sourcePath)) continue;

const navigator = new FileNavigator(content);
this.#cache.set(sourcePath, Promise.resolve(navigator));
}
}

#createNavigator = async (filePath: string) => {
const sourceCode = await fs.readFile(filePath, 'utf8').catch(() => void 0);
return sourceCode == null ? undefined : new FileNavigator(sourceCode);
};

clear() {
this.#cache.clear();
}

static readonly instance = new FileNavigatorCache();
}
1 change: 1 addition & 0 deletions src/utils/__fixtures__/broken.map
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{
1 change: 1 addition & 0 deletions src/utils/__fixtures__/empty.map
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
3 changes: 3 additions & 0 deletions src/utils/__fixtures__/no-content.map
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"sources": ["test.ts"]
}
4 changes: 4 additions & 0 deletions src/utils/__fixtures__/simple.map
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"sources": ["test.ts"],
"sourcesContent": ["export default 42;"]
}
5 changes: 5 additions & 0 deletions src/utils/__fixtures__/with-root.map
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"sources": ["test.ts"],
"sourceRoot": "/home/user/project",
"sourcesContent": ["export default 42;"]
}
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export * from './autoIndent';
export * from './compactObject';
export * from './fastMove';
export * from './FileNavigator';
export * from './FileNavigatorCache';
export * from './getFullExtension';
export * from './getStatusDetails';
export * from './hijackFunction';
Expand Down

0 comments on commit 6d1ff05

Please sign in to comment.