fix(cypress): Test using data-test-id attributes rather than private classes

Makes the test more resilant to inner component changes of the `@nextcloud/vue` library.

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
This commit is contained in:
Ferdinand Thiessen 2023-10-19 16:15:48 +02:00
parent add1d922ba
commit 3f83ae145f
No known key found for this signature in database
GPG key ID: 45FAE7268762B400
11 changed files with 93 additions and 61 deletions

View file

@ -45,6 +45,7 @@
:data-component="UserRow"
:data-sources="filteredUsers"
data-key="id"
data-test-id="userList"
:item-height="rowHeight"
:style="style"
:extra-props="{

View file

@ -36,11 +36,12 @@
:user="user.id" />
</td>
<td class="row__cell row__cell--displayname">
<td class="row__cell row__cell--displayname" data-test-id="cell-displayname">
<template v-if="editing && user.backendCapabilities.setDisplayName">
<NcTextField ref="displayNameField"
data-test="displayNameField"
class="user-row-text-field"
data-test-id="input-displayName"
:data-test-loading="`${loading.displayName}`"
:trailing-button-label="t('settings', 'Submit')"
:class="{ 'icon-loading-small': loading.displayName }"
:show-trailing-button="true"
@ -63,10 +64,13 @@
</template>
</td>
<td class="row__cell"
<td data-test-id="cell-password"
class="row__cell"
:class="{ 'row__cell--obfuscated': hasObfuscated }">
<template v-if="editing && settings.canChangePassword && user.backendCapabilities.setPassword">
<NcTextField class="user-row-text-field"
data-test-id="input-password"
:data-test-loading="`${loading.password}`"
:trailing-button-label="t('settings', 'Submit')"
:class="{'icon-loading-small': loading.password}"
:show-trailing-button="true"
@ -89,7 +93,7 @@
</span>
</td>
<td class="row__cell">
<td class="row__cell" data-test-id="cell-email">
<template v-if="editing">
<NcTextField class="user-row-text-field"
:class="{'icon-loading-small': loading.mailAddress}"
@ -100,7 +104,7 @@
trailing-button-icon="arrowRight"
:value.sync="editedMail"
autocapitalize="off"
autocomplete="new-password"
autocomplete="email"
autocorrect="off"
spellcheck="false"
type="email"
@ -112,7 +116,7 @@
</span>
</td>
<td class="row__cell row__cell--large row__cell--multiline">
<td class="row__cell row__cell--large row__cell--multiline" data-test-id="cell-groups">
<template v-if="editing">
<label class="hidden-visually"
:for="'groups' + uniqueId">
@ -142,6 +146,7 @@
</td>
<td v-if="subAdminsGroups.length > 0 && settings.isAdmin"
data-test-id="cell-subadmins"
class="row__cell row__cell--large row__cell--multiline">
<template v-if="editing && settings.isAdmin && subAdminsGroups.length > 0">
<label class="hidden-visually"
@ -168,7 +173,7 @@
</span>
</td>
<td class="row__cell">
<td class="row__cell" data-test-id="cell-quota">
<template v-if="editing">
<label class="hidden-visually"
:for="'quota' + uniqueId">
@ -200,7 +205,7 @@
<td v-if="showConfig.showLanguages"
class="row__cell row__cell--large"
data-test="language">
data-test-id="cell-language">
<template v-if="editing">
<label class="hidden-visually"
:for="'language' + uniqueId">
@ -224,6 +229,7 @@
</td>
<td v-if="showConfig.showUserBackend || showConfig.showStoragePath"
data-test-id="cell-storageLocation"
class="row__cell row__cell--large">
<template v-if="!isObfuscated">
<span v-if="showConfig.showUserBackend">{{ user.backend }}</span>
@ -238,11 +244,11 @@
<td v-if="showConfig.showLastLogin"
:title="userLastLoginTooltip"
class="row__cell"
data-test="lastLogin">
data-test-id="cell-lastLogin">
<span v-if="!isObfuscated">{{ userLastLogin }}</span>
</td>
<td class="row__cell row__cell--large row__cell--fill">
<td class="row__cell row__cell--large row__cell--fill" data-test-id="cell-manager">
<template v-if="editing">
<label class="hidden-visually"
:for="'manager' + uniqueId">
@ -268,7 +274,7 @@
</span>
</td>
<td class="row__cell row__cell--actions">
<td class="row__cell row__cell--actions" data-test-id="cell-actions">
<UserRowActions v-if="visible && !isObfuscated && canEdit && !loading.all"
:actions="userActions"
:disabled="isLoadingField"

View file

@ -25,7 +25,9 @@
<NcActions :aria-label="t('settings', 'Toggle user actions menu')"
:disabled="disabled"
:inline="1">
<NcActionButton :disabled="disabled"
<NcActionButton data-test-id="button-toggleEdit"
:data-test="`${edit}`"
:disabled="disabled"
@click="toggleEdit">
{{ edit ? t('settings', 'Done') : t('settings', 'Edit') }}
<template #icon>

View file

@ -21,6 +21,7 @@
*/
/// <reference types="cypress-if" />
import { User } from '@nextcloud/cypress'
import { getUserListRow, handlePasswordConfirmation } from './usersUtils'
const admin = new User('admin', 'admin')
const jdoe = new User('jdoe', 'jdoe')
@ -106,10 +107,10 @@ describe('Settings: Create and delete users', function() {
handlePasswordConfirmation(admin.password)
// see that the created user is in the list
cy.get('tbody.user-list__body tr[data-test="john"]').within(() => {
getUserListRow('john')
// see that the list of users contains the user john
cy.contains('john').should('exist')
})
.contains('john')
.should('exist')
})
it('Can delete a user', function() {

View file

@ -22,16 +22,35 @@
/**
* Assert that `element` does not exist or is not visible
*
* Useful in cases such as when NcModal is opened/closed rapidly
* @param element Element that is inspected
*/
export function assertNotExistOrNotVisible(element: JQuery<HTMLElement>) {
const doesNotExist = element.length === 0
const isNotVisible = !element.is(':visible')
// eslint-disable-next-line no-unused-expressions
expect(doesNotExist || isNotVisible, 'does not exist or is not visible').to.be.true
}
/**
* Get the settings users list
* @return Cypress chainable object
*/
export function getUserList() {
return cy.get('[data-test-id="userList"]')
}
/**
* Get the row entry for given userId within the settings users list
*
* @param userId the user to query
* @return Cypress chainable object
*/
export function getUserListRow(userId: string) {
return getUserList().find(`tr[data-test="${userId}"]`)
}
/**
* Handle the confirm password dialog (if needed)
* @param adminPassword The admin password for the dialog

View file

@ -21,7 +21,7 @@
*/
import { User } from '@nextcloud/cypress'
import { assertNotExistOrNotVisible } from './usersUtils.js'
import { assertNotExistOrNotVisible, getUserList } from './usersUtils.js'
const admin = new User('admin', 'admin')
@ -77,8 +77,8 @@ describe('Settings: Show and hide columns', function() {
})
// see that the language column is in all user rows
cy.get('tbody.user-list__body tr').each(($row) => {
cy.wrap($row).get('[data-test="language"]').should('exist')
getUserList().find('tbody tr').each(($row) => {
cy.wrap($row).get('[data-test-id="cell-language"]').should('exist')
})
})
@ -89,8 +89,8 @@ describe('Settings: Show and hide columns', function() {
})
// see that the last login column is in all user rows
cy.get('tbody.user-list__body tr').each(($row) => {
cy.wrap($row).get('[data-test="lastLogin"]').should('exist')
getUserList().find('tbody tr').each(($row) => {
cy.wrap($row).get('[data-test-id="cell-lastLogin"]').should('exist')
})
// open the settings dialog
@ -112,8 +112,8 @@ describe('Settings: Show and hide columns', function() {
})
// see that the last login column is not in all user rows
cy.get('tbody.user-list__body tr').each(($row) => {
cy.wrap($row).get('[data-test="lastLogin"]').should('not.exist')
getUserList().find('tbody tr').each(($row) => {
cy.wrap($row).get('[data-test-id="cell-lastLogin"]').should('not.exist')
})
})
})

View file

@ -21,6 +21,7 @@
*/
import { User } from '@nextcloud/cypress'
import { getUserListRow, handlePasswordConfirmation } from './usersUtils'
const admin = new User('admin', 'admin')
const jdoe = new User('jdoe', 'jdoe')
@ -34,16 +35,14 @@ describe('Settings: Change user properties', function() {
})
beforeEach(function() {
cy.get(`tbody.user-list__body tr[data-test="${jdoe.userId}"]`).within(() => {
// reset edit mode for the user jdoe
cy.get('td.row__cell--actions .action-items > button:first-of-type')
.invoke('attr', 'title')
.then((title) => {
if (title === 'Done') {
cy.get('td.row__cell--actions .action-items > button:first-of-type').click()
}
})
})
// reset to read-only mode: try to find the edit button and click it if set to editing
getUserListRow(jdoe.userId)
.find('[data-test-id="cell-actions"]')
// replace with following (more error resilent) with nextcloud-vue 8
// find('[data-test-id="button-toggleEdit"][data-test="true"]')
.find('button[aria-label="Done"]')
.if()
.click({ force: true })
})
after(() => {
@ -51,41 +50,45 @@ describe('Settings: Change user properties', function() {
})
it('Can change the display name', function() {
cy.get(`tbody.user-list__body tr[data-test="${jdoe.userId}"]`).within(() => {
// see that the list of users contains the user jdoe
cy.contains(jdoe.userId).should('exist')
// see that the list of users contains the user jdoe
getUserListRow(jdoe.userId).should('exist')
// toggle the edit mode for the user jdoe
cy.get('td.row__cell--actions .action-items > button:first-of-type').click()
})
.find('[data-test-id="cell-actions"]')
.find('button[aria-label="Edit"]')
// replace with following (more error resilent) with nextcloud-vue 8
// find('[data-test-id="button-toggleEdit"]')
.click({ force: true })
cy.get(`tbody.user-list__body tr[data-test="${jdoe.userId}"]`).within(() => {
getUserListRow(jdoe.userId).within(() => {
// set the display name
cy.get('input[data-test="displayNameField"]').should('exist').and('have.value', 'jdoe')
cy.get('input[data-test="displayNameField"]').clear()
cy.get('input[data-test="displayNameField"]').type('John Doe')
cy.get('input[data-test="displayNameField"]').should('have.value', 'John Doe')
cy.get('input[data-test="displayNameField"] ~ button').click()
cy.get('input[data-test-id="input-displayName"]').should('exist').and('have.value', 'jdoe')
cy.get('input[data-test-id="input-displayName"]').clear()
cy.get('input[data-test-id="input-displayName"]').type('John Doe')
cy.get('input[data-test-id="input-displayName"]').should('have.value', 'John Doe')
cy.get('input[data-test-id="input-displayName"] ~ button').click()
// Make sure no confirmation modal is shown
handlePasswordConfirmation(admin.password)
// see that the display name cell is done loading
cy.get('.user-row-text-field.icon-loading-small').should('exist')
cy.waitUntil(() => cy.get('.user-row-text-field.icon-loading-small').should('not.exist'), { timeout: 10000 })
cy.get('[data-test-id="input-displayName"]').should('have.attr', 'data-test-loading', 'true')
cy.waitUntil(() => cy.get('[data-test-id="input-displayName"]').should('have.attr', 'data-test-loading', 'false'), { timeout: 10000 })
})
// Success message is shown
cy.get('.toastify.toast-success').contains(/Display.+name.+was.+successfully.+changed/i).should('exist')
})
it('Can change the password', function() {
cy.get(`tbody.user-list__body tr[data-test="${jdoe.userId}"]`).within(() => {
// see that the list of users contains the user jdoe
cy.contains(jdoe.userId).should('exist')
// see that the list of users contains the user jdoe
getUserListRow(jdoe.userId).should('exist')
// toggle the edit mode for the user jdoe
cy.get('td.row__cell--actions .action-items > button:first-of-type').click()
})
.find('[data-test-id="cell-actions"]')
.find('button[aria-label="Edit"]')
// replace with following (more error resilent) with nextcloud-vue 8
// find('[data-test-id="button-toggleEdit"]')
.click({ force: true })
cy.get(`tbody.user-list__body tr[data-test="${jdoe.userId}"]`).within(() => {
getUserListRow(jdoe.userId).within(() => {
// see that the password of user0 is ""
cy.get('input[type="password"]').should('exist').and('have.value', '')
// set the password for user0 to 123456
@ -98,10 +101,10 @@ describe('Settings: Change user properties', function() {
handlePasswordConfirmation(admin.password)
// see that the password cell for user user0 is done loading
cy.get('.user-row-text-field.icon-loading-small').should('exist')
cy.waitUntil(() => cy.get('.user-row-text-field.icon-loading-small').should('not.exist'), { timeout: 10000 })
cy.get('[data-test-id="input-password"]').should('have.attr', 'data-test-loading', 'true')
cy.waitUntil(() => cy.get('[data-test-id="input-password"]').should('have.attr', 'data-test-loading', 'false'), { timeout: 10000 })
// password input is emptied on change
cy.get('input[type="password"]').should('have.value', '')
cy.get('[data-test-id="input-password"]').should('have.value', '')
})
// Success message is shown
cy.get('.toastify.toast-success').contains(/Password.+successfully.+changed/i).should('exist')

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long