javascript - Cypress JSで1つのメソッドで複数のページオブジェクトを使用する
1つのメソッドで複数のページオブジェクトを使うことについて、いくつか論理的に悩んでいます。そのようなロジックを使うアイデアが見つかりませんでした。例えば;
これらは、私のページオブジェクトusersTableの中のメソッドです;
get rolesAndStatusMenu() {
return cy.get("#menu- > .MuiPaper-root > .MuiMenu-list>li");
}
get usersPartialRow() {
return cy.get(".MuiTableBody-root>tr>td");
}
settings(options: string) {
return cy
.get(
"[style='position: fixed; z-index: 1300; inset: 0px;'] > .MuiPaper-root > .MuiList-root",
)
.contains(options);
}
menuButton(userId: string) {
return cy.get(`.user_${userId}>td>button`);
}
userRow(userId?: string) {
const userrow = ".MuiTableBody-root>tr";
if (userId === undefined) {
return cy.get(userrow);
}
return cy.get(userrow).get(`.user_${userId}`);
}
私はこのテストで userRow メソッドを次のように使っています;
usersTable.userRow(userId).should("not.exist");
例えば、このテストで私の userMenu と settings メソッドを使用しています;
usersTable.menuButton(userId).click();
usersTable.settings("Impersonate").click();
私がやりたいけれど、どうすればいいかわからないというアイデアに行きましょう;
usersTable.userRow(userId).settings.menuButton.click()
usersTable.userRow(userId).settings.impersonate.click()
そのように使う方法はありますか?どんなアイデアでも受け付けます
更新
私はもう一つページオブジェクトを持っており、その中で usersPage ページモーダルの中に usersTable コンポーネントモーダルを定義します
import { UsersTable } from "../components/UsersTable ";
export class Users {
visit() {
return cy.visit("/users");
}
get headingText() {
return cy.get(".MuiTypography-h5");
}
get inviteUserBtn() {
return cy.get(".MuiGrid-root> .MuiButtonBase-root");
}
get inviteUserModal() {
return cy.get(".MuiDialogContent-root");
}
get usersTable() {
return new UsersTable();
}
}
だから私のコードはこんな感じです
usersPage.usersTable.menuButton(userId).click();
usersPage.usersTable.settings("Impersonate").click();
usersPage.visit();
usersPage.usersTable.menuButton(userId).click();
usersPage.usersTable.settings("Delete").click();
usersPage.usersTable.userRow(userId).should("not.exist");
例えばこの方法を使って
usersPage.usersTable.userRow(userId).settings.menuButton.click()
だからおそらく私は UsersTable 内にクラスを作成できるかもしれません
export class UsersTable {
...
}
class userTableRow {
}
**and returning it in `UsersTable` or something like that ?**
2回目の更新
今、私は UsersTable ファイルの中にクラスを作成します;
class UserRow {
userRow(userId?: string) {
const userrow = ".MuiTableBody-root>tr";
if (userId === undefined) {
return cy.get(userrow);
}
return cy.get(userrow).find(`.user_${userId}`);
}
get menuButton() {
return this.userRow(`>td>button`); //Btw im not sure this one is working, i think something is wrong here;
}
get impersonate() {
return cy
.get(
"[style='position: fixed; z-index: 1300; inset: 0px;'] > .MuiPaper-root > .MuiList-root",
)
.contains("Impersonate");
}
get delete() {
return cy
.get(
"[style='position: fixed; z-index: 1300; inset: 0px;'] > .MuiPaper-root > .MuiList-root",
)
.contains("Delete");
}
}
そして、このクラス returned を UsersTable クラスで使用するために;
userRow(userId?: string) {
const userrow = ".MuiTableBody-root>tr";
if (userId === undefined) {
return cy.get(userrow);
}
return new UserRow(userId); **// but got error, it says Expected 0 arguments, but got 1.**
}
もし私がこのコメント欄のように使ったら;
// get UserRow() {
// return new UserRow();
// }
私はユーザーの中のすべてにアクセスすることができますが、このようにテストを使うことはできません;
usersPage.usersTable.UserRow(userId).settings.menuButton.click()
あるいは
usersPage.usersTable.UserRow.userRow(userId).settings.menuButton.click()
しかし、このように使うことができます;
usersPage.usersTable.UserRow.menuButton.click()
UserRow の userId?: string をどのように定義できますか。userId は毎回常に変わるので、テスト内で API から取得しています。そのため、確実に定義することができません。
回答 #1
クラスのメソッドを流れるようにするには、this を返す必要があります。
class UsersTable {
settings(options: string) {
cy.get(...).contains(options);
return this // call next method on return value
}
menuButton(userId: string) {
return cy.get(`.user_${userId}>td>button`); // cannot call another after this one
}
userRow(userId?: string) {
cy.get(userrow).get(`.user_${userId}`);
return this; // call next method on return value
}
}
うまくいくはずです,
usersTable.userRow(userId).settings().menuButton().click()
しかし今、最初の二つの方法の値はどこにありますか?
それらをクラスに保存する必要があります
class UsersTable {
userId: string = '';
setting: string = '';
settings(options: string) {
cy.get(...).contains(options)
.invoke('text')
.then(text => this.setting = text)
return this
}
menuButton() { // no parameter, take value from field
return cy.get(`.user_${this.setting}>td>button`);
}
userRow() { // no parameter, take value from field
cy.get(userrow).get(`.user_${this.userId}`)
.invoke('text')
.then(text => this.userId = text)
return this; // call next method on return value
}
}
しかし今では柔軟性を失い、方法は密接に結びついており、もはや独立していません。
回答 #2
UserTable のメソッドは Cypress.Chainable 型を返すので、次のメソッドに渡すには結果をアンラップする必要があります。
また、要素を返していますが、次のメソッドではテキストコンテンツが必要なので、それも抽出してください。
usersTable.userRow(userId)
.then((userIdElement: JQuery<HTMLElement>) => { // unwrap Chainable
const text = userIdElement.text() // extract text
usersTable.settings(text)
})
.then((settingsElement: JQuery<HTMLElement>) => { // unwrap Chainable
const text = settingsElement.text() // extract text
usersTable.menuButton(text).click()
})
もし任意の要素がHTMLInputElementの場合は、代わりにuserIdElement.val()を使用してください。
userRow()への調整:
class UsersTable {
...
userRow(userId?: string): Cypress.Chainable<JQuery<HTMLElement>> {
const userrow = ".MuiTableBody-root>tr";
if (userId === undefined) {
return cy.get(userrow);
}
return cy.get(userrow)
.find(`.user_${userId}`) // use find instead of get
}
}
pageObject の代わりにカスタムコマンドでそれを行う方法
チェイニングはCypressのコマンドにおける自然なコードパターンであり、カスタムコマンドを使用することも同様です
commands.js
/// <reference types="cypress" />
declare namespace Cypress {
interface Chainable<Subject = any> {
settings(options?: string): Chainable<JQuery<HTMLElement>>;
menuButton(userId?: string): Chainable<JQuery<HTMLElement>>;
userRow(userId?: string): Chainable<JQuery<HTMLElement>>;
}
}
Cypress.Commands.add('settings', {prevSubject: 'optional'}, (subject: any, options?: string): Cypress.Chainable<JQuery<HTMLElement>> => {
if (options === undefined) {
options = subject as string;
}
return cy.get("[style='position: fixed; z-index: 1300; inset: 0px;'] > .MuiPaper-root > .MuiList-root")
.contains(options)
})
Cypress.Commands.add('menuButton', {prevSubject: 'optional'}, (subject: any, userId?: string): Cypress.Chainable<JQuery<HTMLElement>> => {
if (userId === undefined) {
userId = subject as string;
}
return cy.get(`.user_${userId}>td>button`);
})
Cypress.Commands.add('userRow', (userId?: string): Cypress.Chainable<JQuery<HTMLElement>> => {
const userrow = ".MuiTableBody-root>tr";
if (userId === undefined) {
return cy.get(userrow);
}
return cy.get(userrow)
.find(`.user_${userId}`)
})
テスト
it('tests with userId from userRow()', () => {
const userId = '1'
cy.userRow(userId)
.settings() // as child command, userId from previous command
.menuButton()
.click()
});
it('tests with userId hard-coded', () => {
cy.settings('abc') // as parent command, userId passed as parameter
.menuButton()
.click()
});
