How to create and use a custom ability #2740
-
I have this test file import { beforeEach, describe, it } from '@serenity-js/playwright-test';
import { chromium } from '@playwright/test';
import { engage } from '@serenity-js/core';
import { Actors } from '../../../screenplay/actors';
import { AuthenticateWithRxApi } from '../../../screenplay/tasks/auth/authenticateWithRxApi';
import { buildOrchestratorMockRequestFromEventFile } from '../../../screenplay/interactions/submission/orchestrator/buildOrchestratorMockRequestFromEventFile';
describe('BDX Orchestrator Submission', () => {
beforeEach(async () => {
const browser = await chromium.launch();
engage(new Actors(browser, { baseURL: 'https://stage.accelerant.ai' }));
});
describe('when a bordereau submission comes through the orchestrator', () => {
describe('with a valid bordereau file', () => {
it('is committed into snowflake successfully', async ({ actorCalled }) => {
await actorCalled('Borden').attemptsTo(
AuthenticateWithRxApi.using(
process.env.ACCELERANT_ADMIN_EMAIL as string,
process.env.ACCELERANT_ADMIN_PASS as string,
),
buildOrchestratorMockRequestFromEventFile('trilogy-written-gbp-bdx-orchestrator-record.json'),
);
});
});
});
}); and this cast of actors i use... import { Browser } from '@playwright/test';
import { Actor, Cast, TakeNotes } from '@serenity-js/core';
import { BrowseTheWebWithPlaywright, PlaywrightOptions } from '@serenity-js/playwright';
import { CallAnApi } from '@serenity-js/rest';
import path from 'node:path';
import { BuildMockOrchestratorEvent } from './abilities/submission/orchestrator/buildMockOrchestratorEvent';
export class Actors implements Cast {
constructor(private readonly browser: Browser, private readonly options: PlaywrightOptions) {}
prepare(actor: Actor): Actor {
switch (actor.name) {
case 'Borden':
return actor.whoCan(
BrowseTheWebWithPlaywright.using(this.browser, this.options),
CallAnApi.at(this.options.baseURL as string),
TakeNotes.usingAnEmptyNotepad(),
BuildMockOrchestratorEvent.from(path.resolve(__dirname, '../test-data/submission/bdx/events')),
);
default:
return actor.whoCan(
BrowseTheWebWithPlaywright.using(this.browser, this.options),
TakeNotes.usingAnEmptyNotepad(),
);
}
}
} and I made this custom ability... import { v4 as uuidv4 } from 'uuid';
import { Ability, Answerable, AnswersQuestions } from '@serenity-js/core';
import { EventRecord, EventRequest } from '../../../../utils/submission/orchestrator/eventTypes';
import path from 'node:path';
import * as fs from 'node:fs';
export class BuildMockOrchestratorEvent extends Ability {
private mockRecords: { Records: EventRecord[] } | undefined;
/**
* Creates an ability instance to build a mock event request from files in `eventDir`
*/
static from(eventDir: string): BuildMockOrchestratorEvent {
return new BuildMockOrchestratorEvent(eventDir);
}
public constructor(private readonly eventDir: string) {
super();
}
public getMockRecords(): { Records: EventRecord[] } | undefined {
return this.mockRecords;
}
/**
* Builds the mock event request by resolving the Answerable file name.
*/
public async buildMockRequest(actor: AnswersQuestions, fileName: Answerable<string>): Promise<EventRequest> {
const resolvedFileName = await actor.answer(fileName);
const eventContent = this.getFileContentsAsJson(resolvedFileName) as Record<string, any>;
const eventRecord: EventRecord = {
messageId: eventContent.messageId || uuidv4(),
receiptHandle: eventContent.receiptHandle || uuidv4(),
body: JSON.stringify(eventContent.body),
attributes: eventContent.attributes || {
ApproximateReceiveCount: '1',
AWSTraceHeader: uuidv4(),
SentTimestamp: new Date().toISOString(),
SequenceNumber: '0',
MessageGroupId: uuidv4(),
SenderId: 'test-sender',
MessageDeduplicationId: uuidv4(),
ApproximateFirstReceiveTimestamp: `${Date.now()}`,
},
messageAttributes: eventContent.messageAttributes || {},
md5OfBody: eventContent.md5OfBody || 'dummy-md5',
eventSource: eventContent.eventSource || 'aws:sqs',
eventSourceARN: eventContent.eventSourceARN ||
'arn:aws:sqs:eu-west-1:9876543210:de-dev-bdx-foo.fifo',
awsRegion: eventContent.awsRegion || 'eu-west-1',
};
this.mockRecords = { Records: [eventRecord] };
return this.mockRecords;
}
private getFileContentsAsJson(fileName: string): Record<string, any> {
const filePath = path.join(this.eventDir, fileName);
const fileContents = fs.readFileSync(filePath, 'utf-8');
return JSON.parse(fileContents);
}
} But when I use it in this interaction... import { Answerable, Interaction, notes, the } from '@serenity-js/core';
import { BuildMockOrchestratorEvent } from '../../../abilities/submission/orchestrator/buildMockOrchestratorEvent';
import { mockOrchestratorRecords } from '../../../questions/submission/bdx/orchestrator/mockOrchestratorRecords';
export const buildOrchestratorMockRequestFromEventFile = (fileName: Answerable<string>) =>
Interaction.where(the`#actor builds a mock request to represent the BDX orchestrator`, async (actor) => {
await BuildMockOrchestratorEvent.as(actor).buildMockRequest(actor, fileName);
notes().set('mock_orchestrator_records', mockOrchestratorRecords());
}); I get this error...
Appreciate any help on why its not registering my custom ability. Thanks. |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
Fixed with this...
|
Beta Was this translation helpful? Give feedback.
-
Playwright Test uses fixtures to inject dependencies into test scenarios. Serenity/JS leverages this mechanism to configure the framework's object graph, so the top-level The The configuration from the example doesn't work because To solve it, follow the example from the import { beforeEach, describe, it, test } from '@serenity-js/playwright-test';
import { chromium } from '@playwright/test';
import { engage } from '@serenity-js/core';
import { Actors } from '../../../screenplay/actors';
import { AuthenticateWithRxApi } from '../../../screenplay/tasks/auth/authenticateWithRxApi';
import { buildOrchestratorMockRequestFromEventFile } from '../../../screenplay/interactions/submission/orchestrator/buildOrchestratorMockRequestFromEventFile';
describe('BDX Orchestrator Submission', () => {
test.use({
actors: ({ browser, contextOptions }, use) => {
const cast = Cast.where(actor =>
actor.whoCan(
BrowseTheWebWithPlaywright.using(browser, contextOptions),
CallAnApi.at(this.options.baseURL as string),
TakeNotes.usingAnEmptyNotepad(),
BuildMockOrchestratorEvent.from(path.resolve(__dirname, '../test-data/submission/bdx/events')),
)
)
// Make sure to pass your custom cast to Playwright `use` callback
use(cast)
},
})
describe('when a bordereau submission comes through the orchestrator', () => {
describe('with a valid bordereau file', () => {
it('is committed into snowflake successfully', async ({ actorCalled }) => {
await actorCalled('Borden').attemptsTo(
AuthenticateWithRxApi.using(
process.env.ACCELERANT_ADMIN_EMAIL as string,
process.env.ACCELERANT_ADMIN_PASS as string,
),
buildOrchestratorMockRequestFromEventFile('trilogy-written-gbp-bdx-orchestrator-record.json'),
);
});
});
});
}); Alternatively, you can also add the ability to an actor inside the test scenario: describe('BDX Orchestrator Submission', () => {
describe('when a bordereau submission comes through the orchestrator', () => {
describe('with a valid bordereau file', () => {
it('is committed into snowflake successfully', async ({ actorCalled }) => {
await actorCalled('Borden')
.whoCan(
BuildMockOrchestratorEvent.from(path.resolve(__dirname, '../test-data/submission/bdx/events')),
)
.attemptsTo(
AuthenticateWithRxApi.using(
process.env.ACCELERANT_ADMIN_EMAIL as string,
process.env.ACCELERANT_ADMIN_PASS as string,
),
buildOrchestratorMockRequestFromEventFile('trilogy-written-gbp-bdx-orchestrator-record.json'),
);
});
});
});
}); |
Beta Was this translation helpful? Give feedback.
Playwright Test uses fixtures to inject dependencies into test scenarios. Serenity/JS leverages this mechanism to configure the framework's object graph, so the top-level
serenity
object, as well as the cast ofactors
and so on. With Playwright Test, each worker process receives its own instance of theserenity
object graph.The
engage
function is intended to support test runners such as Mocha, Jasmine or Cucumber that don't offer any built-in dependency injection mechanisms. This function configures a global instance of theserenity
object.The configuration from the example doesn't work because
beforeEach
configures a global instance ofserenity
, while the test uses has its own instance.…