Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@ interface RenderParams<A extends object = Dictionary> {

/** @see [[RenderContent]] */
content?: Dictionary<RenderContent | RenderContentFn | string>;

/**
* List of component events to listen to
*/
events?: string[];
}

/**
Expand Down
12 changes: 11 additions & 1 deletion src/core/prelude/test-env/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type iStaticPage from 'super/i-static-page/i-static-page';
import type { ComponentElement } from 'super/i-static-page/i-static-page';

import { expandedParse } from 'core/prelude/test-env/components/json';
import EventStore from 'core/prelude/test-env/event-store';

globalThis.renderComponents = (
componentName: string,
Expand Down Expand Up @@ -73,10 +74,19 @@ globalThis.renderComponents = (
const
ids = scheme.map(() => Math.random());

const vNodes = scheme.map(({attrs, content}, i) => ctx.$createElement(componentName, {
const vNodes = scheme.map(({attrs, content, events}, i) => ctx.$createElement(componentName, {
attrs: {
'v-attrs': {
...attrs,

...events?.reduce((acc, name) => ({
...acc,
[name.startsWith('@') ? name : `@${name}`]: (el, ...args) => {
el.component.tmp.eventStore ??= new EventStore();
el.component.tmp.eventStore.push({name, args});
}
}), {}),

[idAttrs]: ids[i]
}
},
Expand Down
52 changes: 52 additions & 0 deletions src/core/prelude/test-env/event-store/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import type { EventStoreEntry } from 'core/prelude/test-env/event-store/interface';

export * from 'core/prelude/test-env/event-store/interface';

export default class EventStore {
events: EventStoreEntry[] = [];
updateListeners: Set<Function> = new Set();

push(event: EventStoreEntry): void {
this.events.push(event);
this.updateListeners.forEach((listener) => listener.call(this, event));
}

waitEvent(targetEvent: EventStoreEntry, timeout?: number): Promise<boolean> {
const
compareEvents = (event1: EventStoreEntry, event2: EventStoreEntry): boolean => Object.fastCompare(event1, event2);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

вместо fastCompare использовать вариант из мр Данила


const
hasEvent: boolean = this.events.some((event) => compareEvents(targetEvent, event));

if (hasEvent || timeout === 0) {
return Promise.resolve(hasEvent);
}

return new Promise<boolean>((resolve) => {
const
clearFns: Function[] = [];

const resolveWith = (val: boolean) => {
clearFns.forEach((fn) => fn.call(this));
resolve(val);
};

const listener = (event: EventStoreEntry) => {
if (compareEvents(targetEvent, event)) {
return resolveWith(true);
}
};

this.updateListeners.add(listener);
clearFns.push(() => this.updateListeners.delete(listener));

if (timeout != null) {
const timerId = setTimeout(() => {
resolveWith(false);
}, timeout);

clearFns.push(() => clearTimeout(timerId));
}
});
}
}
7 changes: 7 additions & 0 deletions src/core/prelude/test-env/event-store/interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* Component event
*/
export interface EventStoreEntry {
name: string;
args: any[];
}
65 changes: 65 additions & 0 deletions tests/helpers/component/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import type iBlock from 'super/i-block/i-block';

import BOM, { WaitForIdleOptions } from 'tests/helpers/bom';

import type { EventStoreEntry } from 'core/prelude/test-env/event-store';
import type EventStore from 'core/prelude/test-env/event-store';

/**
* Class provides API to work with components on a page
*/
Expand Down Expand Up @@ -237,6 +240,68 @@ export default class Component {
return component;
}

/**
* Returns all events emitted by the component that were listened to
*
* @param ctx
* @param componentSelector
*/
static async getComponentEmittedEvents(ctx: Page | ElementHandle, componentSelector: string): Promise<EventStoreEntry[]> {
const
component = await this.waitForComponentByQuery(ctx, componentSelector);

return component.evaluate<EventStoreEntry[], iBlock & {tmp: {eventStore: EventStore}}>(
(ctx) => ctx.unsafe.tmp.eventStore.events
);
}

/**
* Waits until the component emits specified event
*
* @param ctx
* @param componentSelector
* @param eventName
* @param [eventArgs]
*/
static async waitForComponentEvent(
ctx: Page | ElementHandle,
componentSelector: string,
eventName: string,
...eventArgs: any[]
): Promise<boolean>;

/**
* Waits until the component emits specified event
*
* @param ctx
* @param componentSelector
* @param event
* @param [opts]
*/
static async waitForComponentEvent(
ctx: Page | ElementHandle,
componentSelector: string,
event: EventStoreEntry,
opts?: {timeout?: number}
): Promise<boolean>;

static async waitForComponentEvent(
ctx: Page | ElementHandle,
componentSelector: string,
...args: any[]
): Promise<boolean> {
const
[event, opts] = Object.isString(args[0]) ? [{name: args[0], args: args.slice(1)}, {}] : args,
component = await this.waitForComponentByQuery(ctx, componentSelector);

return component.evaluate(
(ctx, {event, opts}) => Boolean(
(<{eventStore: EventStore}>ctx.unsafe.tmp).eventStore?.waitEvent(event, opts?.timeout)
),
{event, opts}
);
}

/**
* Waits until a component by the passed selector switches to the specified status, then returns it
*
Expand Down