thiagoelg commented on code in PR #3556: URL: https://github.com/apache/incubator-kie-tools/pull/3556#discussion_r3226147345
########## packages/bpmn-editor/tests-e2e/__fixtures__/propertiesPanel/gatewayPropertiesPanel.ts: ########## @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Page } from "@playwright/test"; +import { PropertiesPanelBase } from "./propertiesPanelBase"; +import { Diagram } from "../diagram"; +import { NameProperties } from "./parts/nameProperties"; +import { DocumentationProperties } from "./parts/documentationProperties"; + +export class GatewayPropertiesPanel extends PropertiesPanelBase { + private nameProperties: NameProperties; + private documentationProperties: DocumentationProperties; + + constructor( + public diagram: Diagram, + public page: Page + ) { + super(diagram, page); + this.nameProperties = new NameProperties(this.panel(), page); + this.documentationProperties = new DocumentationProperties(this.panel(), page); + } + + public async setName(args: { newName: string }) { + await this.nameProperties.setName({ ...args }); + } + + public async getName(): Promise<string> { + return await this.nameProperties.getName(); + } + + public async setDocumentation(args: { newDocumentation: string }) { + await this.documentationProperties.setDocumentation({ ...args }); + } + + public async getDocumentation(): Promise<string> { + return await this.documentationProperties.getDocumentation(); + } Review Comment: (this is a nitpick) Instead of creating wrapper functions, you can make the `nameProperties` and `documentationProperties` public, then tests can call them directly. ########## packages/bpmn-editor/tests-e2e/__fixtures__/propertiesPanel/gatewayPropertiesPanel.ts: ########## @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Page } from "@playwright/test"; +import { PropertiesPanelBase } from "./propertiesPanelBase"; +import { Diagram } from "../diagram"; +import { NameProperties } from "./parts/nameProperties"; +import { DocumentationProperties } from "./parts/documentationProperties"; + +export class GatewayPropertiesPanel extends PropertiesPanelBase { + private nameProperties: NameProperties; + private documentationProperties: DocumentationProperties; + + constructor( + public diagram: Diagram, + public page: Page + ) { + super(diagram, page); + this.nameProperties = new NameProperties(this.panel(), page); + this.documentationProperties = new DocumentationProperties(this.panel(), page); + } + + public async setName(args: { newName: string }) { + await this.nameProperties.setName({ ...args }); + } + + public async getName(): Promise<string> { + return await this.nameProperties.getName(); + } + + public async setDocumentation(args: { newDocumentation: string }) { + await this.documentationProperties.setDocumentation({ ...args }); + } + + public async getDocumentation(): Promise<string> { + return await this.documentationProperties.getDocumentation(); + } Review Comment: Even better, since all `{nodeName}PropertiesPanel.ts` fixtures include the `nameProperties` and `documentationProperties`, you could add them to the `PropertiesPanelBase` class and constructor. ########## packages/bpmn-editor/tests-e2e/__fixtures__/nodes.ts: ########## @@ -0,0 +1,393 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { expect, Locator, Page } from "@playwright/test"; +import { Diagram } from "./diagram"; +import { EdgeType } from "./edges"; + +export enum NodeType { + START_EVENT = "node_startEvent", + INTERMEDIATE_CATCH_EVENT = "node_intermediateCatchEvent", + INTERMEDIATE_THROW_EVENT = "node_intermediateThrowEvent", + END_EVENT = "node_endEvent", + TASK = "node_task", + CALL_ACTIVITY = "node_callActivity", + SUB_PROCESS = "node_subProcess", + GATEWAY = "node_gateway", + DATA_OBJECT = "node_dataObject", + TEXT_ANNOTATION = "node_textAnnotation", + GROUP = "node_group", + LANE = "node_lane", +} + +export enum DefaultNodeName { + START_EVENT = "", // Events use ID as label, not a name + INTERMEDIATE_CATCH_EVENT = "", // Events use ID as label, not a name + INTERMEDIATE_THROW_EVENT = "", // Events use ID as label, not a name + END_EVENT = "", // Events use ID as label, not a name + TASK = "New Task", + CALL_ACTIVITY = "New Call Activity", + SUB_PROCESS = "New Sub-process", + GATEWAY = "", // Gateways use ID as label when no name is set + DATA_OBJECT = "New Data Object", + TEXT_ANNOTATION = "", // Text annotations use their text content + GROUP = "", // Groups use ID as label + LANE = "New Lane", +} + +export enum NodePosition { + BOTTOM, + CENTER, + LEFT, + RIGHT, + TOP, + TOP_PADDING, +} + +export class Nodes { + constructor( + public page: Page, + public diagram: Diagram, + public browserName: string + ) {} + + public get(args: { name: string }) { + return this.page.locator(`div[data-nodelabel="${args.name}"]`); + } + + public async getId(args: { name: string }): Promise<string> { + return (await this.get({ name: args.name }).getAttribute("data-nodehref")) ?? ""; + } + + public getByType(type: NodeType) { + return this.page.locator(`div[data-nodetype="${type}"]`); + } + + public async getIdByType(type: NodeType): Promise<string> { + const node = this.getByType(type).first(); + await node.waitFor({ state: "attached" }); + return (await node.getAttribute("data-nodehref")) ?? ""; + } + + public async delete(args: { name: string }) { + await this.select({ name: args.name, position: NodePosition.TOP_PADDING }); + await this.diagram.get().press("Delete"); + } + + public async deleteMultiple(args: { names: string[] }) { + await this.selectMultiple({ names: args.names, position: NodePosition.TOP_PADDING }); + await this.diagram.get().press("Delete"); + } + + public async dragNewConnectedEdge(args: { type: EdgeType; from: string; to: string; position?: NodePosition }) { + const fromIsId = args.from.startsWith("_"); + const toIsId = args.to.startsWith("_"); + + const from = fromIsId ? this.getById({ id: args.from }) : this.get({ name: args.from }); + const to = toIsId ? this.getById({ id: args.to }) : this.get({ name: args.to }); + + await from.scrollIntoViewIfNeeded(); + await to.scrollIntoViewIfNeeded(); + + if (fromIsId) { + await this.selectById({ id: args.from, position: NodePosition.TOP }); + } else { + await this.select({ name: args.from, position: NodePosition.TOP }); + } + + const targetPosition = + args.position !== undefined + ? await this.getPositionalNodeHandleCoordinates({ node: to, position: args.position }) + : undefined; + + await from.getByTitle(this.getAddEdgeTitle(args.type)).dragTo(to, { + targetPosition, + force: true, + noWaitAfter: true, + }); + + await this.page.waitForTimeout(1000); + } + + public async dragNewConnectedNode(args: { + type: NodeType; + from: string; + targetPosition: { x: number; y: number }; + thenRenameTo?: string; + }) { + const isId = args.from.startsWith("_"); + + let node: Locator; + if (isId) { + await this.selectById({ id: args.from, position: NodePosition.TOP }); + node = this.getById({ id: args.from }); + } else { + await this.select({ name: args.from, position: NodePosition.TOP }); + node = this.get({ name: args.from }); + } + + const isGateway = await node.evaluate((el) => el.classList.contains("kie-bpmn-editor--gateway-node")); + if (isGateway) { + const box = await node.boundingBox(); + if (box) { + await this.page.mouse.move(box.x + box.width - 10, box.y + box.height / 2); + await this.page.waitForTimeout(500); + } + } + + const { addNodeTitle, nodeName } = this.getNewConnectedNodeProperties(args.type); + + await node.getByTitle(addNodeTitle).dragTo(this.diagram.get(), { targetPosition: args.targetPosition }); + + if (nodeName === "") { + await this.page.waitForSelector(`div[data-nodetype="${args.type}"]`, { timeout: 10000, state: "attached" }); + } else { + await this.page.waitForSelector(`div[data-nodelabel="${nodeName}"]`, { timeout: 10000, state: "attached" }); + } + + if (args.thenRenameTo) { + await this.rename({ current: nodeName, new: args.thenRenameTo }); + } + } + + public getById(args: { id: string }) { + return this.page.locator(`div[data-nodehref="${args.id}"]`); + } + + public async selectById(args: { id: string; position?: NodePosition }) { + const node = this.getById({ id: args.id }); + const coordinates = + args.position !== undefined + ? await this.getPositionalNodeHandleCoordinates({ node, position: args.position }) + : undefined; + await node.click({ position: coordinates, force: true }); + + const isGateway = await node.evaluate((el) => el.classList.contains("kie-bpmn-editor--gateway-node")); + + if (isGateway) { + await this.page.waitForTimeout(300); + } else { + await this.waitForNodeToBeFocused({ id: args.id }); + } + } + + public async renameByLocator(args: { + nodeLocator: ReturnType<Page["locator"]>; + newName: string; + needsSelection?: boolean; + }) { + const needsSelection = args.needsSelection ?? true; + + const textbox = args.nodeLocator.getByRole("textbox").first(); + if (await textbox.isVisible()) { + await textbox.fill(args.newName); + await this.diagram.get().press("Enter"); + await this.page.locator(`[data-nodelabel="${args.newName}"]`).waitFor({ state: "attached" }); + return; + } + + await this.page.keyboard.press("Enter"); + await this.page.keyboard.type(args.newName); + await this.diagram.resetFocus(); + await this.page.locator(`[data-nodelabel="${args.newName}"]`).waitFor({ state: "attached" }); + } + + public async rename(args: { current: string; new: string }) { + const node = this.get({ name: args.current }); + + await this.renameByLocator({ + nodeLocator: node, + newName: args.new, + needsSelection: true, + }); + } + + public async resize(args: { nodeName: string; xOffset: number; yOffset: number }) { + const node = this.get({ name: args.nodeName }); + await this.select({ name: args.nodeName, position: NodePosition.CENTER }); + + const resizeHandle = node.locator('[data-handlepos="right"]').first(); + const box = await resizeHandle.boundingBox(); + + if (box) { + await this.page.mouse.move(box.x + box.width / 2, box.y + box.height / 2); + await this.page.mouse.down(); + await this.page.mouse.move(box.x + box.width / 2 + args.xOffset, box.y + box.height / 2 + args.yOffset); + await this.page.mouse.up(); + } + } + + public async select(args: { name: string; position?: NodePosition }) { + const node = this.get({ name: args.name }); + + const position = + args.position !== undefined + ? await this.getPositionalNodeHandleCoordinates({ node, position: args.position }) + : undefined; + + await node.click({ position, force: true }); + } + + public async selectMultiple(args: { names: string[]; position?: NodePosition }) { + if (this.browserName === "webkit") { + await this.page.keyboard.down("Meta"); + } else { + await this.page.keyboard.down("Control"); + } + + for (const name of args.names) { + const node = this.get({ name }); + + const position = + args.position !== undefined + ? await this.getPositionalNodeHandleCoordinates({ node, position: args.position }) + : undefined; + + await node.click({ position, force: true }); + } + + if (this.browserName === "webkit") { + await this.page.keyboard.up("Meta"); + } else { + await this.page.keyboard.up("Control"); + } + } + + public async waitForNodeToBeFocused(args: { name?: string; id?: string }) { + if (args.id) { + await this.page.waitForSelector(`div[data-nodehref="${args.id}"][data-selected="true"]`); + } else if (args.name !== undefined) { + await this.page.waitForSelector(`div[data-nodelabel="${args.name}"][data-selected="true"]`); + } else { + throw new Error("Either name or id must be provided to waitForNodeToBeFocused"); + } + } + + public async morphNode(args: { + nodeLocator: Locator; + targetMorphType: string; + hoverDelay?: number; + exact?: boolean; + }): Promise<void> { + const hoverDelay = args.hoverDelay ?? 300; + const exact = args.exact ?? false; + + const box = await args.nodeLocator.boundingBox(); + if (!box) { + throw new Error("Node not visible - cannot retrieve bounding box for morphing"); + } + + await this.page.mouse.move(box.x + box.width / 2, box.y + box.height / 2); + await this.page.waitForTimeout(hoverDelay); + + const morphingToggle = args.nodeLocator.getByRole("button", { name: /morph/i }); + await expect(morphingToggle).toBeVisible({ timeout: 5000 }); Review Comment: I'm noticing several places where the `timeout` option is enforced to `5000`. This can be achieved by adding this to the playwright.config.ts file: ``` expect: { timeout: 30000, }, ``` After adding this config, you can remove all `{ timeout: 5000 }` from the code. Learn more [here](https://playwright.dev/docs/test-timeouts). ########## packages/bpmn-editor/tests-e2e/__fixtures__/nodes.ts: ########## @@ -0,0 +1,393 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { expect, Locator, Page } from "@playwright/test"; +import { Diagram } from "./diagram"; +import { EdgeType } from "./edges"; + +export enum NodeType { + START_EVENT = "node_startEvent", + INTERMEDIATE_CATCH_EVENT = "node_intermediateCatchEvent", + INTERMEDIATE_THROW_EVENT = "node_intermediateThrowEvent", + END_EVENT = "node_endEvent", + TASK = "node_task", + CALL_ACTIVITY = "node_callActivity", + SUB_PROCESS = "node_subProcess", + GATEWAY = "node_gateway", + DATA_OBJECT = "node_dataObject", + TEXT_ANNOTATION = "node_textAnnotation", + GROUP = "node_group", + LANE = "node_lane", +} + +export enum DefaultNodeName { + START_EVENT = "", // Events use ID as label, not a name + INTERMEDIATE_CATCH_EVENT = "", // Events use ID as label, not a name + INTERMEDIATE_THROW_EVENT = "", // Events use ID as label, not a name + END_EVENT = "", // Events use ID as label, not a name + TASK = "New Task", + CALL_ACTIVITY = "New Call Activity", + SUB_PROCESS = "New Sub-process", + GATEWAY = "", // Gateways use ID as label when no name is set + DATA_OBJECT = "New Data Object", + TEXT_ANNOTATION = "", // Text annotations use their text content + GROUP = "", // Groups use ID as label + LANE = "New Lane", +} + +export enum NodePosition { + BOTTOM, + CENTER, + LEFT, + RIGHT, + TOP, + TOP_PADDING, +} + +export class Nodes { + constructor( + public page: Page, + public diagram: Diagram, + public browserName: string + ) {} + + public get(args: { name: string }) { + return this.page.locator(`div[data-nodelabel="${args.name}"]`); + } + + public async getId(args: { name: string }): Promise<string> { + return (await this.get({ name: args.name }).getAttribute("data-nodehref")) ?? ""; + } + + public getByType(type: NodeType) { + return this.page.locator(`div[data-nodetype="${type}"]`); + } + + public async getIdByType(type: NodeType): Promise<string> { + const node = this.getByType(type).first(); + await node.waitFor({ state: "attached" }); + return (await node.getAttribute("data-nodehref")) ?? ""; + } + + public async delete(args: { name: string }) { + await this.select({ name: args.name, position: NodePosition.TOP_PADDING }); + await this.diagram.get().press("Delete"); + } + + public async deleteMultiple(args: { names: string[] }) { + await this.selectMultiple({ names: args.names, position: NodePosition.TOP_PADDING }); + await this.diagram.get().press("Delete"); + } + + public async dragNewConnectedEdge(args: { type: EdgeType; from: string; to: string; position?: NodePosition }) { + const fromIsId = args.from.startsWith("_"); + const toIsId = args.to.startsWith("_"); + + const from = fromIsId ? this.getById({ id: args.from }) : this.get({ name: args.from }); + const to = toIsId ? this.getById({ id: args.to }) : this.get({ name: args.to }); + + await from.scrollIntoViewIfNeeded(); + await to.scrollIntoViewIfNeeded(); + + if (fromIsId) { + await this.selectById({ id: args.from, position: NodePosition.TOP }); + } else { + await this.select({ name: args.from, position: NodePosition.TOP }); + } + + const targetPosition = + args.position !== undefined + ? await this.getPositionalNodeHandleCoordinates({ node: to, position: args.position }) + : undefined; + + await from.getByTitle(this.getAddEdgeTitle(args.type)).dragTo(to, { + targetPosition, + force: true, + noWaitAfter: true, + }); + + await this.page.waitForTimeout(1000); + } + + public async dragNewConnectedNode(args: { + type: NodeType; + from: string; + targetPosition: { x: number; y: number }; + thenRenameTo?: string; + }) { + const isId = args.from.startsWith("_"); + + let node: Locator; + if (isId) { + await this.selectById({ id: args.from, position: NodePosition.TOP }); + node = this.getById({ id: args.from }); + } else { + await this.select({ name: args.from, position: NodePosition.TOP }); + node = this.get({ name: args.from }); + } + + const isGateway = await node.evaluate((el) => el.classList.contains("kie-bpmn-editor--gateway-node")); + if (isGateway) { + const box = await node.boundingBox(); + if (box) { + await this.page.mouse.move(box.x + box.width - 10, box.y + box.height / 2); + await this.page.waitForTimeout(500); + } + } + + const { addNodeTitle, nodeName } = this.getNewConnectedNodeProperties(args.type); + + await node.getByTitle(addNodeTitle).dragTo(this.diagram.get(), { targetPosition: args.targetPosition }); + + if (nodeName === "") { + await this.page.waitForSelector(`div[data-nodetype="${args.type}"]`, { timeout: 10000, state: "attached" }); + } else { + await this.page.waitForSelector(`div[data-nodelabel="${nodeName}"]`, { timeout: 10000, state: "attached" }); + } + + if (args.thenRenameTo) { + await this.rename({ current: nodeName, new: args.thenRenameTo }); + } + } + + public getById(args: { id: string }) { + return this.page.locator(`div[data-nodehref="${args.id}"]`); + } + + public async selectById(args: { id: string; position?: NodePosition }) { + const node = this.getById({ id: args.id }); + const coordinates = + args.position !== undefined + ? await this.getPositionalNodeHandleCoordinates({ node, position: args.position }) + : undefined; + await node.click({ position: coordinates, force: true }); + + const isGateway = await node.evaluate((el) => el.classList.contains("kie-bpmn-editor--gateway-node")); + + if (isGateway) { + await this.page.waitForTimeout(300); + } else { + await this.waitForNodeToBeFocused({ id: args.id }); + } + } + + public async renameByLocator(args: { + nodeLocator: ReturnType<Page["locator"]>; + newName: string; + needsSelection?: boolean; + }) { + const needsSelection = args.needsSelection ?? true; + + const textbox = args.nodeLocator.getByRole("textbox").first(); + if (await textbox.isVisible()) { + await textbox.fill(args.newName); + await this.diagram.get().press("Enter"); + await this.page.locator(`[data-nodelabel="${args.newName}"]`).waitFor({ state: "attached" }); + return; + } + + await this.page.keyboard.press("Enter"); + await this.page.keyboard.type(args.newName); + await this.diagram.resetFocus(); + await this.page.locator(`[data-nodelabel="${args.newName}"]`).waitFor({ state: "attached" }); + } + + public async rename(args: { current: string; new: string }) { + const node = this.get({ name: args.current }); + + await this.renameByLocator({ + nodeLocator: node, + newName: args.new, + needsSelection: true, + }); + } + + public async resize(args: { nodeName: string; xOffset: number; yOffset: number }) { + const node = this.get({ name: args.nodeName }); + await this.select({ name: args.nodeName, position: NodePosition.CENTER }); + + const resizeHandle = node.locator('[data-handlepos="right"]').first(); + const box = await resizeHandle.boundingBox(); + + if (box) { + await this.page.mouse.move(box.x + box.width / 2, box.y + box.height / 2); + await this.page.mouse.down(); + await this.page.mouse.move(box.x + box.width / 2 + args.xOffset, box.y + box.height / 2 + args.yOffset); + await this.page.mouse.up(); + } + } + + public async select(args: { name: string; position?: NodePosition }) { + const node = this.get({ name: args.name }); + + const position = + args.position !== undefined + ? await this.getPositionalNodeHandleCoordinates({ node, position: args.position }) + : undefined; + + await node.click({ position, force: true }); + } + + public async selectMultiple(args: { names: string[]; position?: NodePosition }) { + if (this.browserName === "webkit") { + await this.page.keyboard.down("Meta"); + } else { + await this.page.keyboard.down("Control"); + } + + for (const name of args.names) { + const node = this.get({ name }); + + const position = + args.position !== undefined + ? await this.getPositionalNodeHandleCoordinates({ node, position: args.position }) + : undefined; + + await node.click({ position, force: true }); + } + + if (this.browserName === "webkit") { + await this.page.keyboard.up("Meta"); + } else { + await this.page.keyboard.up("Control"); + } + } + + public async waitForNodeToBeFocused(args: { name?: string; id?: string }) { + if (args.id) { + await this.page.waitForSelector(`div[data-nodehref="${args.id}"][data-selected="true"]`); + } else if (args.name !== undefined) { + await this.page.waitForSelector(`div[data-nodelabel="${args.name}"][data-selected="true"]`); + } else { + throw new Error("Either name or id must be provided to waitForNodeToBeFocused"); + } + } + + public async morphNode(args: { + nodeLocator: Locator; + targetMorphType: string; + hoverDelay?: number; + exact?: boolean; + }): Promise<void> { + const hoverDelay = args.hoverDelay ?? 300; + const exact = args.exact ?? false; + + const box = await args.nodeLocator.boundingBox(); + if (!box) { + throw new Error("Node not visible - cannot retrieve bounding box for morphing"); + } + + await this.page.mouse.move(box.x + box.width / 2, box.y + box.height / 2); + await this.page.waitForTimeout(hoverDelay); + + const morphingToggle = args.nodeLocator.getByRole("button", { name: /morph/i }); + await expect(morphingToggle).toBeVisible({ timeout: 5000 }); + await morphingToggle.click({ force: true }); + + const morphingPanel = this.page.getByTestId("kie-tools--bpmn-editor--morphing-panel"); + await morphingPanel.waitFor({ state: "visible", timeout: 5000 }); + + const morphingOption = morphingPanel.getByTitle(args.targetMorphType, { exact }); + await expect(morphingOption).toBeVisible({ timeout: 5000 }); + await morphingOption.click({ force: true }); Review Comment: Will this fail if the current Node is already of type `targetMorphType`? (e.g., morphing a User Task node to User Task again). ########## packages/bpmn-editor/tests-e2e/__fixtures__/propertiesPanel/intermediateEventPropertiesPanel.ts: ########## @@ -0,0 +1,241 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { expect, Page } from "@playwright/test"; +import { PropertiesPanelBase } from "./propertiesPanelBase"; +import { Diagram } from "../diagram"; +import { Nodes } from "../nodes"; +import { NameProperties } from "./parts/nameProperties"; +import { DocumentationProperties } from "./parts/documentationProperties"; + +export class IntermediateEventPropertiesPanel extends PropertiesPanelBase { + private nameProperties: NameProperties; + private documentationProperties: DocumentationProperties; + + constructor( + public diagram: Diagram, + public page: Page, + public nodes: Nodes + ) { + super(diagram, page); + this.nameProperties = new NameProperties(this.panel(), page); + this.documentationProperties = new DocumentationProperties(this.panel(), page); + } + + public async setName(args: { newName: string }) { + await this.nameProperties.setName({ ...args }); + } + + public async getName(): Promise<string> { + return await this.nameProperties.getName(); + } + + public async setDocumentation(args: { newDocumentation: string }) { + await this.documentationProperties.setDocumentation({ ...args }); + } + + public async getDocumentation(): Promise<string> { + return await this.documentationProperties.getDocumentation(); + } + + public async selectEventDefinition(args: { eventType: string }) { + const catchEvent = this.page.getByTestId("kie-tools--bpmn-editor--node-intermediate-catch-event"); + const throwEvent = this.page.getByTestId("kie-tools--bpmn-editor--node-intermediate-throw-event"); + + const selectedNode = (await catchEvent.count()) > 0 ? catchEvent.first() : throwEvent.first(); + + await expect(selectedNode).toBeVisible({ timeout: 5000 }); + + await this.nodes.morphNode({ + nodeLocator: selectedNode, + targetMorphType: args.eventType, + }); + } + + public async setTimerDefinition(args: { type: "date" | "duration" | "cycle"; value: string }) { + if (args.type === "duration") { + await this.panel().getByLabel("Fire once after duration").click(); + const valueInput = this.panel().getByPlaceholder("Enter duration or expression #{expression}"); + await valueInput.fill(args.value); + } else if (args.type === "cycle") { + await this.panel().getByLabel("Fire multiple times").click(); + const valueInput = this.panel().getByPlaceholder("Enter time cycle or expression #{expression}"); + await valueInput.fill(args.value); + } else { + await this.panel().getByLabel("Fire at a specific date").click(); + const valueInput = this.panel().getByPlaceholder("Enter date value or expression #{expression}"); + await valueInput.fill(args.value); + } + + await this.page.keyboard.press("Enter"); + } + + public async setMessageDefinition(args: { messageName: string }) { + await this.selectEventDefinition({ eventType: "Message" }); + + await this.panel().getByRole("combobox").first().click(); + await this.page.keyboard.type(args.messageName); + + const createOption = this.page.getByText(`Create Message "${args.messageName}"`, { exact: true }); + if (await createOption.isVisible().catch(() => false)) { + await createOption.click(); + } else { + await this.page.getByRole("option", { name: args.messageName, exact: true }).click(); + } + } + + public async setSignalDefinition(args: { + signalName: string; + scope?: "default" | "processInstance" | "project" | "external"; + }) { + await this.selectEventDefinition({ eventType: "Signal" }); + + await this.panel().getByRole("combobox").first().click(); + await this.page.keyboard.type(args.signalName); + + const createOption = this.page.getByText(`Create Signal "${args.signalName}"`, { exact: true }); + if (await createOption.isVisible().catch(() => false)) { + await createOption.click(); + } else { + await this.page.getByRole("option", { name: args.signalName, exact: true }).click(); + } + + if (args.scope) { + const scopeSelect = this.panel().locator("select").first(); + await scopeSelect.waitFor({ state: "visible", timeout: 10000 }); Review Comment: Can we stick to the default timeout (the one set in the playwright.config.ts)? Or is it really necessary to have different timeouts in these cases? -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
