Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Propose an e2e testing framework #4591

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ machine:
node:
version: 6

dependencies:
pre:
- wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add - ; sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list'; sudo apt-get update; sudo apt-get install google-chrome-stable firefox

test:
override:
- bash build/ci.sh
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@
"build": "node build/build.js",
"build:ssr": "npm run build -- vue.runtime.common.js,vue-server-renderer",
"build:weex": "npm run build -- weex-vue-framework,weex-template-compiler",
"test": "npm run lint && flow check && npm run test:types && npm run test:cover && npm run test:e2e -- --env phantomjs && npm run test:ssr && npm run test:weex",
"test": "npm run lint && flow check && npm run test:types && npm run test:cover && npm run test:e2e:testcafe && npm run test:ssr && npm run test:weex",
"test:unit": "karma start test/unit/karma.unit.config.js",
"test:cover": "karma start test/unit/karma.cover.config.js",
"test:e2e": "npm run build -- vue.min.js && node test/e2e/runner.js",
"test:e2e:testcafe": "npm run build -- vue.min.js && testcafe chrome,firefox test/e2e/tests/*.js",
"test:weex": "npm run build:weex && jasmine JASMINE_CONFIG_PATH=test/weex/jasmine.json",
"test:ssr": "npm run build:ssr && jasmine JASMINE_CONFIG_PATH=test/ssr/jasmine.json",
"test:sauce": "npm run sauce -- 0 && npm run sauce -- 1 && npm run sauce -- 2",
Expand Down Expand Up @@ -79,6 +80,7 @@
"eslint-loader": "^1.7.1",
"eslint-plugin-flowtype": "^2.34.0",
"eslint-plugin-jasmine": "^2.2.0",
"eslint-plugin-testcafe": "^0.2.1",
"eslint-plugin-vue-libs": "^1.2.0",
"file-loader": "^0.11.2",
"flow-bin": "^0.48.0",
Expand Down Expand Up @@ -119,6 +121,7 @@
"serialize-javascript": "^1.3.0",
"shelljs": "^0.7.8",
"typescript": "^2.3.4",
"testcafe": "^0.16.2",
"uglify-js": "^3.0.15",
"webpack": "^2.6.1",
"weex-js-runtime": "^0.20.5",
Expand Down
4 changes: 2 additions & 2 deletions src/core/vdom/vnode.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ export function cloneVNode (vnode: VNode, deep?: boolean): VNode {
cloned.key = vnode.key
cloned.isComment = vnode.isComment
cloned.isCloned = true
if (deep) {
cloned.children = vnode.children && cloneVNodes(vnode.children)
if (deep && vnode.children) {
cloned.children = cloneVNodes(vnode.children)
}
return cloned
}
Expand Down
2 changes: 2 additions & 0 deletions test/e2e/.eslintrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
"plugins": ["testcafe"],
"extends": "plugin:testcafe/recommended",
"rules": {
"indent": 0
}
Expand Down
35 changes: 35 additions & 0 deletions test/e2e/grid-manipulation-results.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export default {
'defaultSort': [
{ name: 'Chuck Norris', power: Infinity },
{ name: 'Bruce Lee', power: 9000 },
{ name: 'Jackie Chan', power: 7000 },
{ name: 'Jet Li', power: 8000 }
],
'byNameDesc': [
{ name: 'Jet Li', power: 8000 },
{ name: 'Jackie Chan', power: 7000 },
{ name: 'Chuck Norris', power: Infinity },
{ name: 'Bruce Lee', power: 9000 }
],
'byPowerAsc': [
{ name: 'Chuck Norris', power: Infinity },
{ name: 'Bruce Lee', power: 9000 },
{ name: 'Jet Li', power: 8000 },
{ name: 'Jackie Chan', power: 7000 }
],
'byPowerDesc': [
{ name: 'Jackie Chan', power: 7000 },
{ name: 'Jet Li', power: 8000 },
{ name: 'Bruce Lee', power: 9000 },
{ name: 'Chuck Norris', power: Infinity }
],
'byNameAsc': [
{ name: 'Bruce Lee', power: 9000 },
{ name: 'Chuck Norris', power: Infinity },
{ name: 'Jackie Chan', power: 7000 },
{ name: 'Jet Li', power: 8000 }
],
'filterByName': [
{ name: 'Chuck Norris', power: Infinity }
]
}
163 changes: 163 additions & 0 deletions test/e2e/page-models.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import { Selector } from 'testcafe'

class CommitCollection {
constructor (selector) {
this.allItems = Selector(selector)
this.ids = this.allItems.find('.commit')
this.messages = this.allItems.find('.message')
}
}

export class CommitsPage {
constructor () {
this.branch = {
current: Selector('p'),
labelForMaster: Selector('label[for="master"]'),
labelForDev: Selector('label[for="dev"]'),
master: Selector('#master'),
dev: Selector('#dev'),
inputs: Selector('input'),
labels: Selector('label')
}

this.commits = new CommitCollection('li')
}
}

export class GridPage {
constructor () {
this.table = Selector('table')
this.query = Selector('input[name="query"]')
this.noMatchesFound = Selector('p')
}
}

export class MarkdownPage {
constructor () {
const editor = Selector('#editor')

this.src = editor.find('textarea')
this.result = editor.find('div').addCustomDOMProperties({
innerHTML: el => el.innerHTML
})
}
}

class ModalControl {
constructor (selector) {
this.mainElement = Selector(selector)
this.wrapper = this.mainElement.find('.modal-wrapper')
this.container = this.wrapper.find('.modal-container')
this.header = this.container.find('.modal-header')
this.body = this.container.find('.modal-body')
this.footer = this.container.find('.modal-footer')
this.defaultButton = this.container.find('.modal-default-button')
}
}

export class ModalPage {
constructor () {
this.showModalBtn = Selector('#show-modal')
this.modal = new ModalControl('.modal-mask')
}
}

class Select2 {
constructor (selector) {
this.fallbackSelect = Selector(selector)
this.container = Selector('span.select2-container')
this.dropdown = this.container.find('.select2-selection__rendered')
this.options = this.container.find('.select2-results__option')
}
}

export class Select2Page {
constructor () {
this.selected = Selector('p')
this.select2 = new Select2('select')
}
}

export class SvgPage {
constructor () {
this.svg = Selector('svg')
this.ranges = Selector('input[type="range"]')
this.labels = Selector('label')
this.buttons = Selector('button')

this.addForm = {
input: Selector('input[name="newlabel"]'),
button: Selector('#add > button')
}
}
}

export class TodomvcPage {
constructor () {
const newTodo = Selector('.new-todo')
const itemsContainer = Selector('.main')
const allItems = itemsContainer.find('li.todo')
const getItemDelete = index => allItems.nth(index).find('.destroy')

this.newTodo = newTodo

this.items = {
container: itemsContainer,
all: allItems,
completed: itemsContainer.find('li.todo.completed'),
getLabel: index => allItems.nth(index).find('label'),
getCheckbox: index => allItems.nth(index).find('.toggle'),
getEdit: index => allItems.nth(index).find('.edit'),
getDelete: getItemDelete,
toggleAll: itemsContainer.find('.toggle-all'),
edited: itemsContainer.find('.todo.editing'),
createNewItem: async function createNewItem (t, text) {
await t.typeText(newTodo, text)
.pressKey('enter')
},
removeItemAt: async function removeItemAt (t, n) {
await t.hover(allItems.nth(n), { offsetX: 10, offsetY: 10 })
.click(getItemDelete(n))
}
}

const footerContainer = Selector('.footer')
const filtersContainer = footerContainer.find('.filters')

this.footer = {
container: footerContainer,
filters: {
all: filtersContainer.find('a').withText('All'),
active: filtersContainer.find('a').withText('Active'),
completed: filtersContainer.find('a').withText('Completed'),
getSelected: () => filtersContainer.find('.selected')
},
clearCompleted: footerContainer.find('.clear-completed'),
countLeftItems: footerContainer.find('.todo-count strong')
}
}
}

class TreeNode {
constructor (selector) {
this.node = Selector(selector)
this.name = this.node.child('div')
this.childNodesContainer = this.node.child('ul')
this.addNew = this.childNodesContainer.child('li.add')
}
}

export class TreePage {
constructor () {
this.allItems = Selector('.item')
this.allAddItems = Selector('.add')
this.allChildFolders = this.allItems.find('ul')

this.createModelFromDomNode = node => new TreeNode(node)

const rootContainer = Selector('#demo')

this.topLevelRoot = this.createModelFromDomNode(rootContainer.child('li'))
}
}

36 changes: 36 additions & 0 deletions test/e2e/testing-helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { ClientFunction } from 'testcafe'

export async function assertTable (t, table, expectedData) {
const columns = ['name', 'power']

await t.expect(table.find('td').count, expectedData.length * columns.length)

let currentTdText = null
let expectedText = null

for (let i = 0; i < expectedData.length; i++) {
for (let j = 0; j < columns.length; j++) {
currentTdText = await table.find('tbody').find('tr').nth(i).find('td').nth(j).textContent
expectedText = expectedData[i][columns[j]]

await t.expect(currentTdText).contains(expectedText)
}
}
}

export async function assertPoligonPoints (t, countPoint) {
const checkPolygonPoints = ClientFunction(() => {
/* eslint-disable no-undef */
var points = stats.map(function (stat, i) {
var point = valueToPoint(stat.value, i, countPoint)
return point.x + ',' + point.y
}).join(' ')
/* eslint-enable no-undef */

return document.querySelector('polygon').attributes[0].value === points
}, {
dependencies: { countPoint }
})

await t.expect(await checkPolygonPoints()).ok()
}
27 changes: 27 additions & 0 deletions test/e2e/tests/commits.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { CommitsPage } from '../page-models'

fixture `Commits`
.page('../../../examples/commits/index.html')

const page = new CommitsPage()
const { branch, commits } = page

test('commits', async t => {
await t
.expect(branch.inputs.count).eql(2)
.expect(branch.labels.count).eql(2)
.expect(branch.labelForMaster.textContent).contains('master')
.expect(branch.labelForDev.textContent).contains('dev')
.expect(branch.master.checked).ok()
.expect(branch.dev.checked).notOk()
.expect(branch.current.textContent).contains('vuejs/vue@master')
.expect(commits.allItems.count).eql(3)
.expect(commits.ids.count).eql(3)
.expect(commits.messages.count).eql(3)

.click(branch.dev)
.expect(branch.current.textContent).contains('vuejs/vue@dev')
.expect(commits.allItems.count).eql(3)
.expect(commits.ids.count).eql(3)
.expect(commits.messages.count).eql(3)
})
41 changes: 41 additions & 0 deletions test/e2e/tests/grid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { GridPage } from '../page-models'
import { assertTable } from '../testing-helpers'
import gridManipulationResults from '../grid-manipulation-results'

fixture `Grid`
.page('../../../examples/grid/index.html')

const page = new GridPage()
const { table, query, noMatchesFound } = page

test('grid', async t => {
await t
.expect(table.find('th').count).eql(2)
.expect(table.find('th.active').count).eql(0)
.expect(table.find('th:nth-child(1)').textContent).contains('Name')
.expect(table.find('th:nth-child(2)').textContent).contains('Power')
await assertTable(t, table, gridManipulationResults.defaultSort)

await t.click(table.find('th').nth(0))
await assertTable(t, table, gridManipulationResults.byNameDesc)

await t.click(table.find('th').nth(1))
await assertTable(t, table, gridManipulationResults.byPowerAsc)

await t.click(table.find('th').nth(1))
await assertTable(t, table, gridManipulationResults.byPowerDesc)

await t.click(table.find('th').nth(0))
await assertTable(t, table, gridManipulationResults.byNameAsc)

await t.typeText(query, 'infinity')
await assertTable(t, table, gridManipulationResults.filterByName)

await t
.pressKey('ctrl+a delete')
.expect(noMatchesFound.count).eql(0)

.typeText(query, 'stringthatdoesnotexistanywhere')
.expect(noMatchesFound.count).eql(1)
})

20 changes: 20 additions & 0 deletions test/e2e/tests/markdown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { MarkdownPage } from '../page-models'

fixture `Markdown`
.page('../../../examples/markdown/index.html')

const page = new MarkdownPage()
const { src, result } = page

test('markdown', async t => {
await t
.expect(src.value).eql('# hello')
.expect(page.result.innerHTML).contains('<h1 id="hello">hello</h1>')

.typeText(src, '\n## foo\n\n- bar\n- baz', { replace: true })
.expect(result.innerHTML).contains('<h1 id="hello">hello</h1>')
.expect(result.innerHTML).contains(
'<h2 id="foo">foo</h2>\n' +
'<ul>\n<li>bar</li>\n<li>baz</li>\n</ul>'
)
})
Loading