-
Notifications
You must be signed in to change notification settings - Fork 357
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test: page model refactor for dropdown and select components [INFENG-694
] (#9362)
- Loading branch information
1 parent
7359504
commit ccf8e7b
Showing
31 changed files
with
603 additions
and
413 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import { BaseComponent, ComponentBasics } from 'e2e/models/BaseComponent'; | ||
import { BasePage } from 'e2e/models/BasePage'; | ||
|
||
interface requiresRoot { | ||
root: BasePage; | ||
} | ||
|
||
interface DropdownArgsWithoutChildNode extends requiresRoot { | ||
childNode?: never; | ||
openMethod: () => Promise<void>; | ||
} | ||
|
||
interface DropdownArgsWithChildNode extends requiresRoot { | ||
childNode: ComponentBasics; | ||
openMethod?: () => Promise<void>; | ||
} | ||
|
||
type DropdownArgs = DropdownArgsWithoutChildNode | DropdownArgsWithChildNode; | ||
|
||
/** | ||
* Returns a representation of the Dropdown component from Ant. | ||
* Until the dropdown component supports test ids, this model will match any open dropdown. | ||
* This constructor represents the contents in antd/es/dropdown/index.d.ts. | ||
* | ||
* The dropdown can be opened by calling the open method. By default, the open method clicks on the child node. Sometimes you might even need to provide both optional arguments, like when a child node is present but impossible to click on due to canvas behavior. | ||
* @param {object} obj | ||
* @param {BasePage} obj.root - root of the page | ||
* @param {ComponentBasics} [obj.childNode] - optional if `openMethod` is present. It's the element we click on to open the dropdown. | ||
* @param {Function} [obj.openMethod] - optional if `childNode` is present. It's the method to open the dropdown. | ||
*/ | ||
export class Dropdown extends BaseComponent { | ||
readonly openMethod: () => Promise<void>; | ||
readonly childNode: ComponentBasics | undefined; | ||
constructor({ root, childNode, openMethod }: DropdownArgs) { | ||
super({ | ||
parent: root, | ||
selector: '.ant-dropdown ul.ant-dropdown-menu:visible', | ||
}); | ||
if (childNode !== undefined) { | ||
this.childNode = childNode; | ||
} | ||
this.openMethod = | ||
openMethod || | ||
(async () => { | ||
if (this.childNode === undefined) { | ||
// We should never be able to throw this error. In the constructor, we | ||
// either provide a childNode or replace this method. | ||
throw new Error('This dropdown does not have a child node to click on.'); | ||
} | ||
await this.childNode.pwLocator.click(); | ||
}); | ||
} | ||
|
||
/** | ||
* Opens the dropdown. | ||
* @returns {Promise<this>} - the dropdown for further actions | ||
*/ | ||
async open(): Promise<this> { | ||
await this.openMethod(); | ||
return this; | ||
} | ||
|
||
/** | ||
* Returns a representation of a dropdown menu item with the specified id. | ||
* @param {string} id - the id of the menu item | ||
*/ | ||
menuItem(id: string): BaseComponent { | ||
return new BaseComponent({ | ||
parent: this, | ||
selector: `li.ant-dropdown-menu-item[data-menu-id$="${id}"]`, | ||
}); | ||
} | ||
|
||
/** | ||
* Returns a representation of a dropdown menu item. Since order is not | ||
* guaranteed, make sure to verify the contents of the menu item. | ||
* | ||
* It's better to prefer the menuItem method and to fall back on this. | ||
* For example, there are some dropdowns which populate with dynamic data. Or | ||
* maybe we could enter some sort of search filter and select the first item. | ||
* @param {number} n - the number of the menu item | ||
*/ | ||
nthMenuItem(n: number): BaseComponent { | ||
return new BaseComponent({ | ||
parent: this, | ||
selector: `li.ant-dropdown-menu-item:nth-of-type(${n})`, | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { BaseComponent, NamedComponent } from 'e2e/models/BaseComponent'; | ||
|
||
/** | ||
* Returns a representation of the Modal component from Ant. | ||
* This constructor represents the contents in antd/es/modal/index.d.ts. | ||
* @param {object} obj | ||
* @param {CanBeParent} obj.parent - The parent used to locate this Modal | ||
* @param {string} obj.selector - Used instead of `defaultSelector` | ||
*/ | ||
export class Modal extends NamedComponent { | ||
readonly defaultSelector = '.ant-modal-content'; | ||
readonly header = new ModalHeader({ parent: this, selector: '.ant-modal-header' }); | ||
readonly body = new BaseComponent({ parent: this, selector: '.ant-modal-body' }); | ||
readonly footer = new ModalFooter({ parent: this, selector: '.ant-modal-footer' }); | ||
} | ||
|
||
/** | ||
* Returns a representation of the Modal's Footer component from Ant. | ||
* This constructor represents the footer in antd/es/modal/index.d.ts.. | ||
* @param {object} obj | ||
* @param {CanBeParent} obj.parent - The parent used to locate this Modal | ||
* @param {string} obj.selector - Used instead of `defaultSelector` | ||
*/ | ||
class ModalHeader extends BaseComponent { | ||
readonly title = new BaseComponent({ parent: this, selector: '.ant-modal-title' }); | ||
} | ||
|
||
/** | ||
* Returns a representation of the Modal's Footer component from Ant. | ||
* This constructor represents the footer in antd/es/modal/index.d.ts.. | ||
* @param {object} obj | ||
* @param {implementsGetLocator} obj.parent - The parent used to locate this Modal | ||
* @param {string} obj.selector - Used instead of `defaultSelector` | ||
*/ | ||
class ModalFooter extends BaseComponent { | ||
readonly submit = new BaseComponent({ parent: this, selector: '[type="submit"]' }); | ||
readonly cancel = new BaseComponent({ parent: this, selector: '[type="cancel"]' }); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { BaseComponent, NamedComponent } from 'e2e/models/BaseComponent'; | ||
|
||
/** | ||
* Returns a representation of the Notification component from Ant. | ||
* This constructor represents the contents in antd/es/notification/index.d.ts. | ||
* @param {object} obj | ||
* @param {CanBeParent} obj.parent - The parent used to locate this Notification | ||
* @param {string} obj.selector - Used instead of `defaultSelector` | ||
*/ | ||
|
||
export class Notification extends NamedComponent { | ||
readonly defaultSelector = '.ant-notification'; | ||
static readonly selectorTopRight = '.ant-notification-topRight'; | ||
static readonly selectorBottomRight = '.ant-notification-bottomRight'; | ||
readonly alert = new BaseComponent({ parent: this, selector: '[role="alert"]' }); | ||
readonly message = new BaseComponent({ | ||
parent: this.alert, | ||
selector: '.ant-notification-notice-message', | ||
}); | ||
readonly description = new BaseComponent({ | ||
parent: this.alert, | ||
selector: '.ant-notification-notice-description', | ||
}); | ||
readonly close = new BaseComponent({ | ||
parent: this, | ||
selector: 'a.ant-notification-notice-close', | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { Locator } from '@playwright/test'; | ||
|
||
import { BaseComponent, NamedComponent } from 'e2e/models/BaseComponent'; | ||
import { Select } from 'e2e/models/hew/Select'; | ||
|
||
/** | ||
* Returns the representation of a Table Pagination from Ant. | ||
* This constructor represents the Table in antd/es/pagination/index.d.ts. | ||
* @param {object} obj | ||
* @param {CanBeParent} obj.parent - The parent used to locate this Pagination | ||
* @param {string} obj.selector - Used as a selector uesd to locate this object | ||
*/ | ||
export class Pagination extends NamedComponent { | ||
readonly defaultSelector = '.ant-pagination'; | ||
readonly previous = new BaseComponent({ | ||
parent: this, | ||
selector: 'li.ant-pagination-prev', | ||
}); | ||
readonly next = new BaseComponent({ | ||
parent: this, | ||
selector: 'li.ant-pagination-next', | ||
}); | ||
readonly #options: BaseComponent = new BaseComponent({ | ||
parent: this, | ||
selector: 'li.ant-pagination-options', | ||
}); | ||
readonly perPage = new PaginationSelect({ | ||
parent: this.#options, | ||
selector: '.ant-pagination-options-size-changer', | ||
}); | ||
pageButtonLocator(n: number): Locator { | ||
return this.pwLocator.locator(`.ant-pagination-item.ant-pagination-item-${n}`); | ||
} | ||
} | ||
|
||
/** | ||
* Returns the representation of a Table Pagination. | ||
* This constructor represents the Table in src/components/Table/Table.tsx. | ||
* @param {object} obj | ||
* @param {parentTypes} obj.parent - The parent used to locate this Pagination | ||
* @param {string} obj.selector - Used as a selector uesd to locate this object | ||
*/ | ||
class PaginationSelect extends Select { | ||
readonly perPage10 = this.menuItem('10 / page'); | ||
readonly perPage20 = this.menuItem('20 / page'); | ||
readonly perPage50 = this.menuItem('50 / page'); | ||
readonly perPage100 = this.menuItem('100 / page'); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { BaseComponent, ComponentBasics } from 'e2e/models/BaseComponent'; | ||
import { BasePage } from 'e2e/models/BasePage'; | ||
|
||
interface requiresRoot { | ||
root: BasePage; | ||
} | ||
|
||
interface PopoverArgsWithoutChildNode extends requiresRoot { | ||
childNode?: never; | ||
openMethod: () => Promise<void>; | ||
} | ||
|
||
interface PopoverArgsWithChildNode extends requiresRoot { | ||
childNode: ComponentBasics; | ||
openMethod?: () => Promise<void>; | ||
} | ||
|
||
type PopoverArgs = PopoverArgsWithoutChildNode | PopoverArgsWithChildNode; | ||
/** | ||
* Returns a representation of the Popover component from Ant. | ||
* Until the popover component supports test ids, this model will match any open popover. | ||
* This constructor represents the contents in antd/es/popover/index.d.ts. | ||
* | ||
* The popover can be opened by calling the open method. By default, the open method clicks on the child node. Sometimes you might even need to provide both optional arguments, like when a child node is present but impossible to click on due to canvas behavior. | ||
* @param {object} obj | ||
* @param {BasePage} obj.root - root of the page | ||
* @param {ComponentBasics} [obj.childNode] - optional if `openMethod` is present. It's the element we click on to open the dropdown. | ||
* @param {Function} [obj.openMethod] - optional if `childNode` is present. It's the method to open the dropdown. | ||
*/ | ||
export class Popover extends BaseComponent { | ||
readonly openMethod: () => Promise<void>; | ||
readonly childNode: ComponentBasics | undefined; | ||
constructor({ root, childNode, openMethod }: PopoverArgs) { | ||
super({ | ||
parent: root, | ||
selector: '.ant-popover .ant-popover-content .ant-popover-inner-content:visible', | ||
}); | ||
if (childNode !== undefined) { | ||
this.childNode = childNode; | ||
} | ||
this.openMethod = | ||
openMethod || | ||
(async () => { | ||
if (this.childNode === undefined) { | ||
// We should never be able to throw this error. In the constructor, we | ||
// either provide a childNode or replace this method. | ||
throw new Error('This popover does not have a child node to click on.'); | ||
} | ||
await this.childNode.pwLocator.click(); | ||
}); | ||
} | ||
|
||
/** | ||
* Opens the popover. | ||
* @returns {Promise<this>} - the popover for further actions | ||
*/ | ||
async open(): Promise<this> { | ||
await this.openMethod(); | ||
return this; | ||
} | ||
} |
Oops, something went wrong.