Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(html-reporter): add testplane support #541

Merged
merged 1 commit into from
Apr 4, 2024

Conversation

KuznetsovRoman
Copy link
Member

@KuznetsovRoman KuznetsovRoman commented Apr 3, 2024

Merging straight to master

What is done

  • add native testplane@0 and testplane@1 support
  • customize help messages (e.g. it suggests to open gui like npx testplane gui if it was launched with npx testplane)
  • add testplane html-reporter plugins support
  • add testplane custom gui support (with doubling testplane property in addition to hermione)

TBD

  • import from "testplane" instead of "hermione"

index.ts Outdated
import {ImagesInfoSaver} from './lib/images-info-saver';
import {getStatus} from './lib/test-adapter/testplane';

export default (testplane: Testplane, opts: Partial<ReporterOptions>): void => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use export default for testplane

hermione.ts Outdated
Comment on lines 2 to 3
const htmlReporter = HtmlReporter.create(config, {toolName: ToolName.Hermione});

(hermione as Hermione & HtmlReporterApi).htmlReporter = htmlReporter;

let isCliCommandLaunched = false;
let handlingTestResults: Promise<void>;
let staticReportBuilder: StaticReportBuilder;

const withMiddleware = <T extends (...args: unknown[]) => unknown>(fn: T):
(...args: Parameters<T>) => ReturnType<T> | undefined => {
return (...args: unknown[]) => {
// If any CLI command was launched, e.g. merge-reports, we need to interrupt regular flow
if (isCliCommandLaunched) {
return;
}

return fn.call(undefined, ...args) as ReturnType<T>;
};
};

hermione.on(hermione.events.CLI, (commander: CommanderStatic) => {
_.values(cliCommands).forEach((command: string) => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
require(path.resolve(__dirname, 'lib/cli-commands', command))(commander, config, hermione);

commander.prependListener(`command:${command}`, () => {
isCliCommandLaunched = true;
});
});
});

hermione.on(hermione.events.INIT, withMiddleware(async () => {
const dbClient = await SqliteClient.create({htmlReporter, reportPath: config.path});
const imageStore = new SqliteImageStore(dbClient);
const expectedPathsCache = new Cache<[TestSpecByPath, string | undefined], string>(getExpectedCacheKey);

const imagesInfoSaver = new ImagesInfoSaver({
imageFileSaver: htmlReporter.imagesSaver,
expectedPathsCache,
imageStore,
reportPath: htmlReporter.config.path
});

staticReportBuilder = StaticReportBuilder.create(htmlReporter, config, {dbClient, imagesInfoSaver});

handlingTestResults = Promise.all([
staticReportBuilder.saveStaticFiles(),
handleTestResults(hermione, staticReportBuilder)
]).then(async () => {
await staticReportBuilder.finalize();
}).then(async () => {
await htmlReporter.emitAsync(htmlReporter.events.REPORT_SAVED, {reportPath: config.path});
});

htmlReporter.emit(htmlReporter.events.DATABASE_CREATED, dbClient.getRawConnection());
}));

hermione.on(hermione.events.RUNNER_START, withMiddleware((runner) => {
staticReportBuilder.registerWorkers(createWorkers(runner as unknown as CreateWorkersRunner));
}));

hermione.on(hermione.events.RUNNER_END, withMiddleware(async () => {
try {
await handlingTestResults;

logPathToHtmlReport(config);
} catch (e: unknown) {
logError(e as Error);
}
}));
};

async function handleTestResults(hermione: Hermione, reportBuilder: StaticReportBuilder): Promise<void> {
return new Promise((resolve, reject) => {
const queue = new PQueue({concurrency: os.cpus().length});
const promises: Promise<unknown>[] = [];

[
{eventName: hermione.events.TEST_PASS},
{eventName: hermione.events.RETRY},
{eventName: hermione.events.TEST_FAIL},
{eventName: hermione.events.TEST_PENDING}
].forEach(({eventName}) => {
type AnyHermioneTestEvent = typeof hermione.events.TEST_PASS;

hermione.on(eventName as AnyHermioneTestEvent, (testResult: HermioneTestResult) => {
promises.push(queue.add(async () => {
const formattedResult = formatTestResult(testResult, getStatus(eventName, hermione.events, testResult));

await reportBuilder.addTestResult(formattedResult);
}).catch(reject));
});
});

hermione.on(hermione.events.RUNNER_END, () => {
return Promise.all(promises).then(() => resolve(), reject);
});
});
}
export = pluginHandler;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Export default export of ./index.ts as module.exports, because old versions of hermione do not support export default

await Promise.all(
_(customGui)
.flatMap<CustomGuiItem>(_.identity)
.map((ctx) => ctx.initialize?.({hermione, ctx}))
.map((ctx) => ctx.initialize?.({testplane, hermione: testplane, ctx}))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

testplane and hermione props are Object.is copies for backward compatibility

const {sectionName, groupIndex, controlIndex} = payload;
const ctx = customGui[sectionName][groupIndex];
const control = ctx.controls[controlIndex];

await ctx.action({hermione, control, ctx});
await ctx.action({testplane, hermione: testplane, control, ctx});
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

testplane and hermione props are Object.is copies for backward compatibility

Comment on lines +108 to +111
const getRegisterFn = tool => `function __${tool}_html_reporter_register_plugin__(p) {return p;};`;

const exec = new Function(`${getRegisterFn('testplane')} ${getRegisterFn('hermione')} return ${code};`);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add support for testplane html reporter plugins

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will work if both are present, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it will.

I tested it locally with "some plugins are built for testplane, while others stay for hermione" and remote with "all plugins are built for hermione": https://nda.ya.ru/t/1ELAEazw75GviJ


export interface HermioneTestResult extends HermioneTestResultOriginal {
export interface HermioneTestResult extends TestResult {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a part of public API, no JSDoc deprecated's is needed

Comment on lines +17 to +18
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface TestplaneTestResult extends HermioneTestResult {}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two are separated, so we could add something to TestplaneTestResult while it still does not exist on HermioneTestResult

Comment on lines +162 to +166
testplane: Testplane,
/**
* @deprecated Use `testplane` instead
*/
hermione: Testplane,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add some JSDoc deprecated comments

Comment on lines 59 to 64
const runHtmlReporter = proxyquire('../../', {
'./lib/sqlite-client': {SqliteClient},
'./lib/server-utils': utils,
'./lib/report-builder/static': {StaticReportBuilder},
'./lib/plugin-api': {HtmlReporter}
});
}).default;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use default-exported value here


createTestRunner = sinon.stub();
createTestRunner = sandbox.stub();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replaced sinon with sandbox in the file, because some other tests (in another files) were failing without it

@KuznetsovRoman KuznetsovRoman changed the title feat: add testplane support feat(html-reporter): add testplane support Apr 4, 2024
@KuznetsovRoman KuznetsovRoman self-assigned this Apr 4, 2024
Copy link
Member

@shadowusr shadowusr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me except making testplane a default export from the package, I am strongly against that.

index.ts Outdated Show resolved Hide resolved
gemini.js Outdated Show resolved Hide resolved
lib/constants/tool-names.ts Outdated Show resolved Hide resolved
Comment on lines +108 to +111
const getRegisterFn = tool => `function __${tool}_html_reporter_register_plugin__(p) {return p;};`;

const exec = new Function(`${getRegisterFn('testplane')} ${getRegisterFn('hermione')} return ${code};`);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will work if both are present, right?

package.json Outdated Show resolved Hide resolved
package.json Outdated Show resolved Hide resolved
@KuznetsovRoman KuznetsovRoman force-pushed the HERMIONE-1492.testplane branch from 98a63e8 to 362a457 Compare April 4, 2024 13:04
@KuznetsovRoman KuznetsovRoman merged commit 2413861 into master Apr 4, 2024
5 checks passed
@KuznetsovRoman KuznetsovRoman deleted the HERMIONE-1492.testplane branch April 4, 2024 13:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Status: Done
Development

Successfully merging this pull request may close these issues.

2 participants