Skip to content

Commit

Permalink
Merge pull request #320 from platformsh-templates/feat/319-e2e-tests
Browse files Browse the repository at this point in the history
Feat/319 e2e tests
  • Loading branch information
chadwcarlson authored Jul 26, 2024
2 parents 1d6ae5d + 0e1244d commit 7ca8f87
Show file tree
Hide file tree
Showing 11 changed files with 2,420 additions and 0 deletions.
17 changes: 17 additions & 0 deletions .cypress/cypress.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const { defineConfig } = require("cypress");

module.exports = defineConfig({
projectId: "ycsbj5",

e2e: {
baseUrl: 'https://localhost',
env: {
environment: 'local',
test_user: 'bobby',
test_user_pass: '123',
},
setupNodeEvents(on, config) {
// implement node event listeners here
},
},
});
88 changes: 88 additions & 0 deletions .cypress/cypress/e2e/editor.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
const newPost = {
"title": "New post from E2E",
"body": "this is a new post created from e2e testing"
}
describe("Editor can log in and post", ()=>{
before(()=>{
cy.createUser(Cypress.env('test_user'))
})

it("can navigate to log in page", ()=>{
cy.visit("/login")
cy.url().should('match',/\//).should('include','wp-login.php')
})


it('Can login via custom function', ()=>{
cy.wplogin(Cypress.env('test_user'),Cypress.env('test_user_pass'));
cy.visit('/wp-admin/')
cy.get('#wp-admin-bar-my-account').contains(Cypress.env('test_user')).should('exist')
})

// it('can navigate to the posts page', ()=>{
// cy.get('#menu-posts').invoke('show')
//
// })

it('Can navigate to new posts page',()=>{
cy.wplogin(Cypress.env('test_user'),Cypress.env('test_user_pass'));
cy.visit('/wp-admin/post-new.php')
cy.get('#editor').should('exist')

})

it('Can add and view a new post ', ()=> {
cy.wplogin(Cypress.env('test_user'),Cypress.env('test_user_pass'))
cy.visit('/wp-admin/post-new.php')
cy.get('button[aria-label="Options"]').click()
cy.get('div[class="components-menu-group"]').find('button').contains('Code editor').click()
cy.get('#inspector-textarea-control-0').type(newPost.title)
cy.get('#post-content-0').type(newPost.body)

//yes, this is brittle but there aren't any id's and very few other identifiers to target these elements
//cy.get('div[class="edit-post-text-editor__toolbar"]').find('button').contains('Exit code editor').click()
cy.get('div[class="editor-text-editor__toolbar"]').find('button').contains('Exit code editor').click()
cy.get('#editor').find('button').contains('Publish').click()
//now we have ANOTHER panel that displays and asks us to click ANOTHER publish button
cy.get('div[class="editor-post-publish-panel"]').as('publishpanel')

cy.get('@publishpanel').find('button').contains('Publish').click()

//now we need to verify it has published
cy.get('@publishpanel').find('a').contains('View Post').as('viewpost').should('exist')
cy.get('@viewpost').click()
cy.get('body')
.find('h1').contains(newPost.title).should('exist')

//cy.get('[aria-label="Add title"]').click().type('Hello!')
})

it('can delete their own posts', ()=>{
// Auth and get session
cy.wplogin(Cypress.env('test_user'),Cypress.env('test_user_pass'))
// Programmatically add a post so we can delete it
cy.addPostForUser(Cypress.env('test_user'),newPost)
cy.visit('/wp-admin/edit.php')
// our post should be here
cy.get('#the-list').find('[data-colname="Title"]').contains(`${newPost.title} test`).should('exist')
// this should yield the a element. we want its parent element that is a td
cy.get('#the-list').find('[data-colname="Title"]').contains(`${newPost.title} test`).as('ourPost')
//cy.get('#the-list').find('[data-colname="Title"]').contains(`${newPost.title} test`).click()
cy.get('@ourPost').parents('td[data-colname="Title"]').as('ourCell')
//
//cy.get('@ourCell').find('div[class="row-actions"]').invoke('show')
cy.get('@ourCell')
.find('span[class="trash"]').contains('Trash')
/**
* @todo find out exactly what event causes this section to be visible
* this anchor is in a div, which has a left setting of -9XXXX. some event then removes that setting allowing it
* to become visible. So far I've been unable to determine the element + event that triggers it to be visible
* setting it to force:true for now since if you're using a screen reader you can still access it with the tab
* key even though it isnt visible
*/
.click({force: true})

cy.get('#message').should('contain','1 post moved to the Trash')

})
})
28 changes: 28 additions & 0 deletions .cypress/cypress/e2e/search.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
describe("Search", ()=>{
beforeEach(()=>{
cy.request({url: '/foobar', failOnStatusCode: false}).its('status').should('equal', 404)
cy.visit('/foobar', {failOnStatusCode: false})
})
context("Search tests", () => {
it("Visits a random page",()=>{
cy.request({url: '/foobar', failOnStatusCode: false}).its('status').should('equal', 404)
cy.visit('/foobar', {failOnStatusCode: false})
cy.get('h1[class="page-title"]').should("exist").contains("Nothing here")
})

it("Runs search from 404", ()=>{
cy.get("#search-form-1").type("Sample{enter}")
//cy.get('[aria-label="Search"]').click()
cy.location().should((loc)=>{
expect(loc.pathname).to.equal('/')
expect(loc.search).to.equal('?s=Sample')
})

cy.get('main').as('main')
cy.get('@main').find('h1').contains('Results for').should('exist')
cy.get('@main').find('h2').contains('Sample Page').should('exist')


})
})
})
4 changes: 4 additions & 0 deletions .cypress/cypress/fixtures/newPost.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"title": "New post from E2E",
"body": "this is a new post created from e2e testing"
}
217 changes: 217 additions & 0 deletions .cypress/cypress/support/commands.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
/**
* Authenticates a user programmatically
*/
Cypress.Commands.add('wplogin',(username,password)=>{

Cypress.log({
name: 'wplogin',
message: `${username}`,
})

cy.session(username, ()=>{
cy.request({
method: 'POST',
url: 'wp-login.php',
form: true,
body: {
log: username,
pwd: password,
redirect_to: '',
rememberme: '',
testcookie: 1,
'wp-submit': 'Log In',
}
})
})
})

/**
* Resets state for our test user
* If a user exists:
* - Removes any stored session data for the user
* - Deletes the user's posts
* - Deletes the user
*/
Cypress.Commands.add('beforeCreateUser', (username) => {
const cmdPrefix = determineOurCmdPrefix()
console.log('starting function beforeCreateUser')
//const cmdUserFind = `${cmdPrefix} wp user list --login="${username}" --field=ID`
console.log('Calling function findUser ')
cy.findUser(username).then((data)=>{
if(data.id !== '') {
console.log(`user ${username} exists and has an ID of ${data.id}`)
//delete their posts before we delete them
console.log('calling function findUserPosts ')
cy.findUserPosts(data.id).then((posts)=>{
if(posts.length > 0) {
console.log(`we have posts to delete for user ${data.id}. List of posts to delete:`)
console.log(posts)
const deletePosts = posts.join(' ')
const cmdDeletePosts = `${cmdPrefix} 'wp post delete ${deletePosts}'`
cy.exec(cmdDeletePosts).its('code').should('eq',0)
}
})
Cypress.session.clearAllSavedSessions()
const cmdUserDelete = `${cmdPrefix} 'wp user delete ${data.id} --yes'`
cy.exec(cmdUserDelete).its('code').should('eq',0)
}
})
})

/**
* Creates a new test user account in our WordPress instance
*/
Cypress.Commands.add('createUser', (username, role="editor") => {
cy.beforeCreateUser(username)
const vendor = Cypress.env('environment')
const cmdPrefix = determineOurCmdPrefix()

const cmdUserCreate = `${cmdPrefix} 'wp user create ${username} ${username}@mail.com --role=${role} --send-email=false --quiet'`

cy.exec(cmdUserCreate).should((response) => {
expect(response.code).to.equal(0)
expect(response.stdout).to.contain('Password:')
}).then(($response)=>{
//console.log('evaluating what the returned password is: ')
//console.log(`stdout is : ${$response.stdout}`)
const pswrd = $response.stdout.match(/^Password: (.*)$/)
//console.log('Setting the new password.')
Cypress.env('test_user_pass',pswrd[1])
cy.findUser(username).then((userdata)=>{
cy.setWelcomePref(userdata.id)
})
})
})

/**
* Retrieves a user id based on their username
*/
Cypress.Commands.add('findUser', (username) => {
console.log('starting function findUser')
const vendor = Cypress.env('environment')
const cmdPrefix = determineOurCmdPrefix()

const cmdUserFind = `${cmdPrefix} 'wp user list --login="${username}" --field=ID'`
cy.exec(cmdUserFind).then((response)=>{
const returnData = {id:''}
if(response.stdout !== ''){
returnData.id = response.stdout
}
console.log('Leaving function findUser')
return returnData
})
})

/**
* Finds the list of posts for a given userID
*/
Cypress.Commands.add('findUserPosts', (userID) => {
const cmdPrefix = determineOurCmdPrefix()
let posts = []
console.log('starting function findUserPosts')
const cmdFindUserPosts = `${cmdPrefix} 'wp post list --field=ID --post_author=${userID} --format=json'`
cy.exec(cmdFindUserPosts).then((response)=>{
/**
* we should get a string in JSON format. if no posts were found we should get []
*/
const possiblePosts = JSON.parse(response.stdout)
if(possiblePosts.length > 0) {
posts = possiblePosts
}

return posts
})
})

/**
* Creates a post for a user
* Used for testing if a user can delete their own posts
*/
Cypress.Commands.add('addPostForUser',(username, postObject)=>{
const cmdPrefix = determineOurCmdPrefix()
console.log(`Adding a post for user ${username}`)
cy.findUser(username).then((user)=>{
expect(user.id).to.not.equal('')
const cmdNewPost = `${cmdPrefix} 'wp post create --post_title="${postObject.title} test" --post_content="${postObject.body}" --post_author=${user.id} --post_status=publish'`
cy.exec(cmdNewPost).its('code').should('eq',0)
})
})

/**
* Sets the user pref to not display the welcome message in the post editor screen
*/
Cypress.Commands.add('setWelcomePref',(userid)=>{
const cmdPrefix = determineOurCmdPrefix()
const userMetaPrefsKey = 'wp_persisted_preferences'
const cmdGetPrefs = `${cmdPrefix} 'wp user meta get ${userid} ${userMetaPrefsKey} --format=json'`
console.log(`Getting user prefs for use ${userid}`)
cy.exec(cmdGetPrefs,{failOnNonZeroExit: false}).then((response)=>{
//expect(response.code).to.eq(0)
let prefs = {}
if ('' != response.stdout) {
prefs = JSON.parse(response.stdout)
}

if(!Object.hasOwn(prefs,'core/edit-post')) {
console.log('prefs does not have core-edit-property. setting.')
//Object.defineProperty(prefs, 'core/edit-post',{})
prefs['core/edit-post'] = {}
}

if(!Object.hasOwn(prefs['core/edit-post'],'welcomeGuide')) {
console.log('prefs.core-edit-post does not have property welcomeGuide. Setting to false')
//Object.defineProperty(prefs['core/edit-post'],'welcomeGuide',false)
prefs['core/edit-post'].welcomeGuide = false
} else {
console.log('prefs.core-edit-post.welcomeGuide exists! Setting to false')
prefs['core/edit-post'].welcomeGuide = false
}

const prefsToSave = JSON.stringify(prefs)
const cmdSavePrefs = `PREFS='${prefsToSave}' && ${cmdPrefix} "wp user meta update ${userid} ${userMetaPrefsKey} '$PREFS' --format=json"`
console.log(`Saving user prefs for user ${userid}`)
cy.exec(cmdSavePrefs).its('code').should('eq',0)
})
})

const determineOurCmdPrefix = () => {
const vendor = Cypress.env('environment')
let cmdPrefix = '';
switch (vendor) {
case 'ddev':
case 'local':
cmdPrefix = 'ddev exec'
break;
default:
cmdPrefix = `${vendor} ssh`
}

return cmdPrefix
}


30 changes: 30 additions & 0 deletions .cypress/cypress/support/e2e.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// ***********************************************************
// This example support/e2e.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************

// Import commands.js using ES2015 syntax:
import './commands'

// Alternatively you can use CommonJS syntax:
// require('./commands')

Cypress.on('uncaught:exception', (err, runnable) => {
// we expect a 3rd party library error with message 'list not defined'
// and don't want to fail the test so we return false
if (err.message.includes("Cannot destructure property 'documentElement' of 'o'")) {
return false
}
// we still want to ensure there are no other unexpected
// errors, so we let them fail the test
})
Loading

0 comments on commit 7ca8f87

Please sign in to comment.