Skip to content

Commit

Permalink
add logStep runtime api
Browse files Browse the repository at this point in the history
  • Loading branch information
baev committed Jul 3, 2024
1 parent 4689203 commit 41e07dd
Show file tree
Hide file tree
Showing 9 changed files with 209 additions and 13 deletions.
7 changes: 6 additions & 1 deletion packages/allure-js-commons/src/facade.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { ContentType } from "./model.js";
import type { Status } from "./model.js";
import { type ContentType } from "./model.js";
import { type AttachmentOptions, type Label, type Link, type ParameterMode, type ParameterOptions } from "./model.js";
import { LabelName, LinkType } from "./model.js";
import { getGlobalTestRuntimeWithAutoconfig } from "./sdk/runtime/runtime.js";
Expand Down Expand Up @@ -98,6 +99,10 @@ const stepContext: () => StepContext = () => ({
},
});

export const logStep = (name: string, status?: Status, error?: Error): PromiseLike<void> => {
return callRuntimeMethod("logStep", name, status, error);
};

export const step = <T = void>(name: string, body: (context: StepContext) => T | PromiseLike<T>): PromiseLike<T> => {
return callRuntimeMethod("step", name, () => body(stepContext()));
};
Expand Down
1 change: 1 addition & 0 deletions packages/allure-js-commons/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export {
layer,
link,
links,
logStep,
owner,
parameter,
parentSuite,
Expand Down
43 changes: 33 additions & 10 deletions packages/allure-js-commons/src/sdk/runtime/MessageTestRuntime.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import type {
AttachmentOptions,
Label,
LabelName,
Link,
LinkType,
ParameterMode,
ParameterOptions,
import {
type AttachmentOptions,
type Label,
type LabelName,
type Link,
type LinkType,
type ParameterMode,
type ParameterOptions,
Status,
} from "../../model.js";
import { Status } from "../../model.js";
import type { RuntimeMessage } from "../types.js";
import { getStatusFromError } from "../utils.js";
import { getMessageAndTraceFromError, getStatusFromError } from "../utils.js";
import type { TestRuntime } from "./types.js";

export abstract class MessageTestRuntime implements TestRuntime {
Expand Down Expand Up @@ -139,6 +139,29 @@ export abstract class MessageTestRuntime implements TestRuntime {
});
}

async logStep(name: string, status: Status = Status.PASSED, error?: Error) {
const timestamp = Date.now();
await this.sendMessage({
type: "step_start",
data: {
name,
start: timestamp,
},
});
await this.sendMessage({
type: "step_stop",
data: {
status: status,
stop: timestamp,
statusDetails: error
? {
...getMessageAndTraceFromError(error),
}
: undefined,
},
});
}

async step<T = void>(name: string, body: () => T | PromiseLike<T>) {
await this.sendMessage({
type: "step_start",
Expand Down
4 changes: 4 additions & 0 deletions packages/allure-js-commons/src/sdk/runtime/NoopTestRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ export class NoopTestRuntime implements TestRuntime {
await this.warning();
}

async logStep() {
await this.warning();
}

async step<T>(name: string, body: () => T | PromiseLike<T>): Promise<T> {
await this.warning();
return body();
Expand Down
4 changes: 3 additions & 1 deletion packages/allure-js-commons/src/sdk/runtime/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { AttachmentOptions, Label, Link, ParameterMode, ParameterOptions } from "../../model.js";
import type { AttachmentOptions, Label, Link, ParameterMode, ParameterOptions, Status } from "../../model.js";

export interface TestRuntime {
labels: (...labels: Label[]) => PromiseLike<void>;
Expand All @@ -21,6 +21,8 @@ export interface TestRuntime {

attachmentFromPath: (name: string, path: string, options: Omit<AttachmentOptions, "encoding">) => PromiseLike<void>;

logStep: (name: string, status?: Status, error?: Error) => PromiseLike<void>;

step: <T = void>(name: string, body: () => T | PromiseLike<T>) => PromiseLike<T>;

stepDisplayName: (name: string) => PromiseLike<void>;
Expand Down
71 changes: 71 additions & 0 deletions packages/allure-js-commons/test/facade.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { type Mocked, describe, expect, it, vi } from "vitest";
import { logStep } from "../src/facade.js";
import { Status } from "../src/model.js";
import { type TestRuntime } from "../src/sdk/runtime/index.js";

const mockRuntime = (): Mocked<TestRuntime> => {
return {
attachment: vi.fn(),
attachmentFromPath: vi.fn(),
description: vi.fn(),
descriptionHtml: vi.fn(),
displayName: vi.fn(),
historyId: vi.fn(),
labels: vi.fn(),
links: vi.fn(),
logStep: vi.fn(),
parameter: vi.fn(),
step: vi.fn(),
stepDisplayName: vi.fn(),
stepParameter: vi.fn(),
testCaseId: vi.fn(),
} as Mocked<TestRuntime>;
};

describe("logStep", () => {
it("should log step", async () => {
const runtime = mockRuntime();
vi.stubGlobal("allureTestRuntime", () => runtime);

await logStep("log step");

const [name, status, error] = runtime.logStep.mock.calls[0];
expect(name).toEqual("log step");
expect(status).toBeUndefined();
expect(error).toBeUndefined();
});
it("should log step with status", async () => {
const runtime = mockRuntime();
vi.stubGlobal("allureTestRuntime", () => runtime);

await logStep("passed step", Status.PASSED);

const [name, status, error] = runtime.logStep.mock.calls[0];
expect(name).toEqual("passed step");
expect(status).toEqual(Status.PASSED);
expect(error).toBeUndefined();
});
it("should log step with status failed", async () => {
const runtime = mockRuntime();
vi.stubGlobal("allureTestRuntime", () => runtime);

await logStep("failed step", Status.FAILED);

const [name, status, error] = runtime.logStep.mock.calls[0];
expect(name).toEqual("failed step");
expect(status).toEqual(Status.FAILED);
expect(error).toBeUndefined();
});
it("should log step with error", async () => {
const runtime = mockRuntime();
vi.stubGlobal("allureTestRuntime", () => runtime);

const err = new Error("some error");
await logStep("failed step", Status.FAILED, err);

const [name, status, error] = runtime.logStep.mock.calls[0];
expect(name).toEqual("failed step");
expect(status).toEqual(Status.FAILED);
expect(error).toEqual(err);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { describe, expect, it, vi } from "vitest";
import { Status } from "../../../src/model.js";
import { MessageTestRuntime } from "../../../src/sdk/runtime/MessageTestRuntime.js";

const implementMessageTestRuntime = () =>
new (class extends MessageTestRuntime {
sendMessage = vi
.fn<Parameters<MessageTestRuntime["sendMessage"]>, ReturnType<MessageTestRuntime["sendMessage"]>>()
.mockImplementation(() => Promise.resolve());
})();

describe("logStep", () => {
it("should create step with name", async () => {
const messageTestRuntime = implementMessageTestRuntime();

await messageTestRuntime.logStep("some step name");

expect(messageTestRuntime.sendMessage).toBeCalledTimes(2);

const [[message1], [message2]] = messageTestRuntime.sendMessage.mock.calls;
expect(message1).toEqual({
type: "step_start",
data: expect.objectContaining({
name: "some step name",
}),
});
expect(message2).toEqual({
type: "step_stop",
data: expect.objectContaining({
status: Status.PASSED,
}),
});
});
it("should create step with name and status", async () => {
const messageTestRuntime = implementMessageTestRuntime();
await messageTestRuntime.logStep("failed step", Status.FAILED);

expect(messageTestRuntime.sendMessage).toBeCalledTimes(2);

const [[message1], [message2]] = messageTestRuntime.sendMessage.mock.calls;
expect(message1).toEqual({
type: "step_start",
data: expect.objectContaining({
name: "failed step",
}),
});
expect(message2).toEqual({
type: "step_stop",
data: expect.objectContaining({
status: Status.FAILED,
}),
});
});
it("should set correct step timings", async () => {
const messageTestRuntime = implementMessageTestRuntime();
const before = Date.now();
await messageTestRuntime.logStep("broken step", Status.BROKEN);
const after = Date.now();

expect(messageTestRuntime.sendMessage).toBeCalledTimes(2);

const [[message1], [message2]] = messageTestRuntime.sendMessage.mock.calls;
expect(message1.data.start).toBeGreaterThanOrEqual(before);
expect(message1.data.start).toBeLessThanOrEqual(after);
expect(message1.data.start).toEqual(message2.data.stop);
});
});
2 changes: 1 addition & 1 deletion packages/allure-playwright/src/runtime.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import test from "@playwright/test";
import { test } from "@playwright/test";
import type { AttachmentOptions } from "allure-js-commons";
import type { RuntimeMessage } from "allure-js-commons/sdk";
import { ALLURE_RUNTIME_MESSAGE_CONTENT_TYPE } from "allure-js-commons/sdk/reporter";
Expand Down
23 changes: 23 additions & 0 deletions packages/allure-playwright/test/spec/runtime/modern/steps.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,26 @@ it("handles nested lambda steps", async () => {
stage: Stage.FINISHED,
});
});

it("should support log steps", async () => {
const { tests } = await runPlaywrightInlineTest({
"sample.test.ts": `
import { test } from '@playwright/test';
import { logStep } from "allure-js-commons";
test("steps", async () => {
await logStep("failed log step", "failed");
});
`,
});

const [testResult] = tests;
expect(testResult.steps).toEqual(
expect.arrayContaining([
expect.objectContaining({
name: "failed log step",
status: Status.FAILED,
}),
]),
);
});

0 comments on commit 41e07dd

Please sign in to comment.