start migrating to testcafe
This commit is contained in:
parent
b0a8ce1efb
commit
13a2195035
1624
package-lock.json
generated
1624
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -60,6 +60,7 @@
|
||||||
"svelte-loader": "^2.3.3",
|
"svelte-loader": "^2.3.3",
|
||||||
"svelte-transitions": "^1.1.1",
|
"svelte-transitions": "^1.1.1",
|
||||||
"svgo": "^1.0.3",
|
"svgo": "^1.0.3",
|
||||||
|
"testcafe": "^0.19.0-alpha1",
|
||||||
"timeago.js": "^3.0.2",
|
"timeago.js": "^3.0.2",
|
||||||
"tiny-queue": "^0.2.1",
|
"tiny-queue": "^0.2.1",
|
||||||
"uglifyjs-webpack-plugin": "^1.1.5",
|
"uglifyjs-webpack-plugin": "^1.1.5",
|
||||||
|
|
54
tests/fixtures.js
Normal file
54
tests/fixtures.js
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
import times from 'lodash/times'
|
||||||
|
|
||||||
|
export const homeTimeline = [
|
||||||
|
{content: 'pinned toot 1'},
|
||||||
|
{content: 'notification of unlisted message'},
|
||||||
|
{content: 'notification of followers-only message'},
|
||||||
|
{content: 'notification of direct message'},
|
||||||
|
{content: 'this is unlisted'},
|
||||||
|
{content: 'this is followers-only'},
|
||||||
|
{content: 'direct'},
|
||||||
|
{spoiler: 'kitten CW'},
|
||||||
|
{content: 'secret video'},
|
||||||
|
{content: "here's a video"},
|
||||||
|
{spoiler: 'CW'},
|
||||||
|
{content: "here's a secret animated kitten gif"},
|
||||||
|
{content: "here's an animated kitten gif"},
|
||||||
|
{content: "here's 2 kitten photos"},
|
||||||
|
{content: "here's a secret kitten"},
|
||||||
|
{content: "here's a kitten"},
|
||||||
|
{content: 'hello admin'},
|
||||||
|
{content: 'hello foobar'},
|
||||||
|
{content: 'hello world'}
|
||||||
|
].concat(times(30, i => ({content: (30 - i).toString()})))
|
||||||
|
|
||||||
|
export const localTimeline = [
|
||||||
|
{spoiler: 'kitten CW'},
|
||||||
|
{content: 'secret video'},
|
||||||
|
{content: "here's a video"},
|
||||||
|
{spoiler: 'CW'},
|
||||||
|
{content: "here's a secret animated kitten gif"},
|
||||||
|
{content: "here's an animated kitten gif"},
|
||||||
|
{content: "here's 2 kitten photos"},
|
||||||
|
{content: "here's a secret kitten"},
|
||||||
|
{content: "here's a kitten"},
|
||||||
|
{content: 'hello world'}
|
||||||
|
].concat(times(30, i => ({content: (30 - i).toString()})))
|
||||||
|
|
||||||
|
export const notifications = [
|
||||||
|
{favoritedBy: 'admin'},
|
||||||
|
{rebloggedBy: 'admin'},
|
||||||
|
{content: 'notification of unlisted message'},
|
||||||
|
{content: 'notification of followers-only message'},
|
||||||
|
{content: 'notification of direct message'},
|
||||||
|
{followedBy: 'quux'},
|
||||||
|
{content: 'hello foobar'},
|
||||||
|
{followedBy: 'admin'}
|
||||||
|
]
|
||||||
|
|
||||||
|
export const favorites = [
|
||||||
|
{content: 'notification of direct message'},
|
||||||
|
{content: 'notification of followers-only message'},
|
||||||
|
{content: 'notification of unlisted message'},
|
||||||
|
{content: 'pinned toot 1'}
|
||||||
|
]
|
33
tests/spec/01-basic-spec.js
Normal file
33
tests/spec/01-basic-spec.js
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import { Selector as $ } from 'testcafe'
|
||||||
|
import { getUrl, settingsButton } from '../utils'
|
||||||
|
|
||||||
|
fixture `01-basic-spec.js`
|
||||||
|
.page `http://localhost:4002`
|
||||||
|
|
||||||
|
test('has the correct <h1>', async t => {
|
||||||
|
await t
|
||||||
|
.expect($('.container h1').innerText).eql('Pinafore')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('navigates to about', async t => {
|
||||||
|
await t
|
||||||
|
.click(settingsButton)
|
||||||
|
.expect(getUrl()).contains('/settings')
|
||||||
|
.click('a[href="/settings/about"]')
|
||||||
|
.expect(getUrl()).contains('/about')
|
||||||
|
.expect($('.container h1').innerText).eql('About Pinafore')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('navigates to /settings/instances/add', async t => {
|
||||||
|
await t.click($('a').withText('log in to an instance'))
|
||||||
|
.expect(getUrl()).contains('/settings/instances/add')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('navigates to settings/instances', async t => {
|
||||||
|
await t.click(settingsButton)
|
||||||
|
.expect(getUrl()).contains('/settings')
|
||||||
|
.click($('a').withText('Instances'))
|
||||||
|
.expect(getUrl()).contains('/settings/instances')
|
||||||
|
.expect($('.container').innerText)
|
||||||
|
.contains("You're not logged in to any instances")
|
||||||
|
})
|
37
tests/spec/02-login-spec.js
Normal file
37
tests/spec/02-login-spec.js
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import { Selector as $ } from 'testcafe'
|
||||||
|
import { addInstanceButton, getUrl, instanceInput, login, settingsButton } from '../utils'
|
||||||
|
|
||||||
|
fixture `02-login-spec.js`
|
||||||
|
.page `http://localhost:4002`
|
||||||
|
|
||||||
|
const formError = $('.form-error')
|
||||||
|
test('Cannot log in to a fake instance', async t => {
|
||||||
|
|
||||||
|
await t.click($('a').withText('log in to an instance'))
|
||||||
|
.typeText(instanceInput, 'fake.nolanlawson.com')
|
||||||
|
.click(addInstanceButton)
|
||||||
|
.expect(formError.exists).ok()
|
||||||
|
.expect(formError.innerText).contains('Is this a valid Mastodon instance?')
|
||||||
|
.typeText(instanceInput, '.biz')
|
||||||
|
.expect(formError.exists).notOk()
|
||||||
|
.typeText(instanceInput, 'fake.nolanlawson.com', {replace: true})
|
||||||
|
.expect(formError.exists).ok()
|
||||||
|
.expect(formError.innerText).contains('Is this a valid Mastodon instance?')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Logs in to localhost:3000', async t => {
|
||||||
|
await login(t, 'foobar@localhost:3000', 'foobarfoobar')
|
||||||
|
.expect($('article.status-article').exists).ok()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Logs out', async t => {
|
||||||
|
await login(t, 'foobar@localhost:3000', 'foobarfoobar')
|
||||||
|
.click(settingsButton)
|
||||||
|
.click($('a').withText('Instances'))
|
||||||
|
.click($('a').withText('localhost:3000'))
|
||||||
|
.expect(getUrl()).contains('/settings/instances/localhost:3000')
|
||||||
|
.click($('button').withText('Log out'))
|
||||||
|
.click($('#modal-dialog button').withText('OK'))
|
||||||
|
.expect($('.container').innerText)
|
||||||
|
.contains("You're not logged in to any instances")
|
||||||
|
})
|
53
tests/spec/03-basic-timeline-spec.js
Normal file
53
tests/spec/03-basic-timeline-spec.js
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
import { Selector as $ } from 'testcafe'
|
||||||
|
import { getUrl, login, validateTimeline } from '../utils'
|
||||||
|
import { homeTimeline, notifications, localTimeline, favorites } from '../fixtures'
|
||||||
|
|
||||||
|
fixture `03-basic-timeline-spec.js`
|
||||||
|
.page `http://localhost:4002`
|
||||||
|
.beforeEach(async t => {
|
||||||
|
await login(t, 'foobar@localhost:3000', 'foobarfoobar')
|
||||||
|
})
|
||||||
|
|
||||||
|
const firstArticle = $('.virtual-list-item[aria-hidden=false] .status-article')
|
||||||
|
|
||||||
|
test('Shows the home timeline', async t => {
|
||||||
|
await t
|
||||||
|
.expect(firstArticle.hasAttribute('aria-setsize')).ok()
|
||||||
|
.expect(firstArticle.getAttribute('aria-posinset')).eql('0')
|
||||||
|
|
||||||
|
await validateTimeline(t, homeTimeline)
|
||||||
|
|
||||||
|
await t.expect(firstArticle.getAttribute('aria-setsize')).eql('49')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Shows notifications', async t => {
|
||||||
|
await t.click($('nav a[aria-label=Notifications]'))
|
||||||
|
.expect(getUrl()).contains('/notifications')
|
||||||
|
|
||||||
|
await validateTimeline(t, notifications)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Shows the local timeline', async t => {
|
||||||
|
await t.click($('nav a[aria-label=Local]'))
|
||||||
|
await t.expect(getUrl()).contains('/local')
|
||||||
|
|
||||||
|
await validateTimeline(t, localTimeline)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Shows the federated timeline', async t => {
|
||||||
|
await t.click($('nav a[aria-label=Community]'))
|
||||||
|
.expect(getUrl()).contains('/community')
|
||||||
|
.click($('a').withText('Federated'))
|
||||||
|
.expect(getUrl()).contains('/federated')
|
||||||
|
|
||||||
|
await validateTimeline(t, localTimeline) // local is same as federated in this case
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Shows favorites', async t => {
|
||||||
|
await t.click($('nav a[aria-label=Community]'))
|
||||||
|
.expect(getUrl()).contains('/community')
|
||||||
|
.click($('a').withText('Favorites'))
|
||||||
|
.expect(getUrl()).contains('/favorites')
|
||||||
|
|
||||||
|
await validateTimeline(t, favorites)
|
||||||
|
})
|
57
tests/utils.js
Normal file
57
tests/utils.js
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import { ClientFunction as exec, Selector as $ } from 'testcafe'
|
||||||
|
|
||||||
|
export const settingsButton = $('nav a[aria-label=Settings]')
|
||||||
|
export const instanceInput = $('#instanceInput')
|
||||||
|
export const addInstanceButton = $('.add-new-instance button')
|
||||||
|
|
||||||
|
export const getUrl = exec(() => window.location.href)
|
||||||
|
|
||||||
|
export function login(t, username, password) {
|
||||||
|
return t.click($('a').withText('log in to an instance'))
|
||||||
|
.expect(getUrl()).contains('/settings/instances/add')
|
||||||
|
.typeText(instanceInput, 'localhost:3000')
|
||||||
|
.click(addInstanceButton)
|
||||||
|
.expect(getUrl()).eql('http://localhost:3000/auth/sign_in')
|
||||||
|
.typeText($('input#user_email'), username)
|
||||||
|
.typeText($('input#user_password'), password)
|
||||||
|
.click($('button[type=submit]'))
|
||||||
|
.expect(getUrl()).contains('/oauth/authorize')
|
||||||
|
.click($('button[type=submit]:not(.negative)'))
|
||||||
|
.expect(getUrl()).eql('http://localhost:4002/')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNthVirtualArticle (n) {
|
||||||
|
return $(`.virtual-list-item[aria-hidden="false"] article[aria-posinset="${n}"]`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function validateTimeline(t, timeline) {
|
||||||
|
for (let i = 0; i < timeline.length; i++) {
|
||||||
|
let status = timeline[i]
|
||||||
|
if (status.content) {
|
||||||
|
await t.expect(getNthVirtualArticle(i).find('.status-content p').innerText)
|
||||||
|
.contains(status.content)
|
||||||
|
}
|
||||||
|
if (status.spoiler) {
|
||||||
|
await t.expect(getNthVirtualArticle(i).find('.status-spoiler p').innerText)
|
||||||
|
.contains(status.spoiler)
|
||||||
|
}
|
||||||
|
if (status.followedBy) {
|
||||||
|
await t.expect(getNthVirtualArticle(i).find('.status-header span').innerText)
|
||||||
|
.contains(status.followedBy + ' followed you')
|
||||||
|
}
|
||||||
|
if (status.rebloggedBy) {
|
||||||
|
await t.expect(getNthVirtualArticle(i).find('.status-header span').innerText)
|
||||||
|
.contains(status.rebloggedBy + ' boosted your status')
|
||||||
|
}
|
||||||
|
if (status.favoritedBy) {
|
||||||
|
await t.expect(getNthVirtualArticle(i).find('.status-header span').innerText)
|
||||||
|
.contains(status.favoritedBy + ' favorited your status')
|
||||||
|
}
|
||||||
|
|
||||||
|
// hovering forces TestCafé to scroll to that element: https://git.io/vABV2
|
||||||
|
if (i % 3 === 2) { // only scroll every nth element
|
||||||
|
await t.hover(getNthVirtualArticle(i))
|
||||||
|
await t.expect($('.loading-footer').exist).notOk()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue