The more your tests resemble the way your software is used, the more confidence they can give you.
conduit - project real world app
{
"articles": [
{
"title": "Article 1",
"slug": "article-1",
"body": "This is article 1 body",
"createdAt": "2020-06-22T11:20:16.812Z",
"updatedAt": "2020-06-22T11:20:16.812Z",
"tagList": ["tag1", "tag2"],
"description": "This is article 1 description",
"author": {
"username": "user1",
"bio": null,
"image": "https://s3.amazonaws.com/uifaces/faces/twitter/nilshelmersson/128.jpg",
"following": false
},
"favorited": false,
"favoritesCount": 0
},
{
"title": "Article 2",
"slug": "article-2",
"body": "This is article 2",
"createdAt": "2020-06-28T11:22:35.833Z",
"updatedAt": "2020-06-28T11:22:35.833Z",
"tagList": ["tag1"],
"description": "This is article 2 description",
"author": {
"username": "user1",
"bio": null,
"image": "https://s3.amazonaws.com/uifaces/faces/twitter/praveen_vijaya/128.jpg",
"following": false
},
"favorited": true,
"favoritesCount": 1
},
{
"title": "Article 3",
"slug": "article-3",
"body": "This is article 3",
"createdAt": "2020-06-25T00:49:44.430Z",
"updatedAt": "2020-06-25T00:49:44.430Z",
"tagList": [],
"description": "This is article 3 description",
"author": {
"username": "user2",
"bio": null,
"image": "https://s3.amazonaws.com/uifaces/faces/twitter/nilshelmersson/128.jpg",
"following": false
},
"favorited": false,
"favoritesCount": 0
}
],
"articlesCount": 3
}
describe('Global Feed', () => {
beforeEach(() => {
cy.server()
cy.route('**/api/articles**', 'fx:global_feed.json').as('getArticles')
})
it('Should show all articles correctly', () => {
cy.visit('/')
cy.get('div.article-preview').should('have.length', 3)
// Article 1 start
cy.get('div.article-preview')
.eq(0)
.find('div.article-meta > a > img')
.should(
'have.attr',
'src',
'https://s3.amazonaws.com/uifaces/faces/twitter/praveen_vijaya/128.jpg'
)
cy.get('div.article-preview')
.eq(0)
.find('div.article-meta > div.info > a.author')
.should('have.text', 'user1')
cy.get('div.article-preview')
.eq(0)
.find('div.article-meta > div.info > span.date')
.should('have.text', 'Mon Jun 22 2020')
cy.get('div.article-preview')
.eq(0)
.find('div.article-meta > div.pull-xs-right > button.btn')
.should('contain', '0')
cy.get('div.article-preview')
.eq(0)
.find('a.preview-link > h1')
.should('have.text', 'Article 1')
cy.get('div.article-preview')
.eq(0)
.find('a.preview-link > p')
.should('have.text', 'This is article 1 description')
cy.get('div.article-preview')
.eq(0)
.find('a.preview-link > ul.tag-list > li.tag-default')
.eq(0)
.should('have.text', 'tag1')
cy.get('div.article-preview')
.eq(0)
.find('a.preview-link > ul.tag-list > li.tag-default')
.eq(1)
.should('have.text', 'tag2')
// Article 1 end
// Article 2 start
cy.get('div.article-preview')
.eq(1)
.find('div.article-meta > a > img')
.should(
'have.attr',
'src',
'https://s3.amazonaws.com/uifaces/faces/twitter/praveen_vijaya/128.jpg'
)
cy.get('div.article-preview')
.eq(1)
.find('div.article-meta > div.info > a.author')
.should('have.text', 'user1')
cy.get('div.article-preview')
.eq(1)
.find('div.article-meta > div.info > span.date')
.should('have.text', 'Sun Jun 28 2020')
cy.get('div.article-preview')
.eq(1)
.find('div.article-meta > div.pull-xs-right > button.btn')
.should('contain', '1')
cy.get('div.article-preview')
.eq(1)
.find('a.preview-link > h1')
.should('have.text', 'Article 2')
cy.get('div.article-preview')
.eq(1)
.find('a.preview-link > p')
.should('have.text', 'This is article 2 description')
cy.get('div.article-preview')
.eq(1)
.find('a.preview-link > ul.tag-list > li.tag-default')
.eq(0)
.should('have.text', 'tag1')
// Article 2 end
// Article 3 start
cy.get('div.article-preview')
.eq(2)
.find('div.article-meta > a > img')
.should(
'have.attr',
'src',
'https://s3.amazonaws.com/uifaces/faces/twitter/nilshelmersson/128.jpg'
)
cy.get('div.article-preview')
.eq(2)
.find('div.article-meta > div.info > a.author')
.should('have.text', 'user2')
cy.get('div.article-preview')
.eq(2)
.find('div.article-meta > div.info > span.date')
.should('have.text', 'Thu Jun 25 2020')
cy.get('div.article-preview')
.eq(2)
.find('div.article-meta > div.pull-xs-right > button.btn')
.should('contain', '0')
cy.get('div.article-preview')
.eq(2)
.find('a.preview-link > h1')
.should('have.text', 'Article 3')
cy.get('div.article-preview')
.eq(2)
.find('a.preview-link > p')
.should('have.text', 'This is article 3 description')
cy.get('div.article-preview')
.eq(2)
.find('a.preview-link > ul.tag-list')
.should('be.empty')
// Article 3 end
})
})
implicit -> explicit relationships의 전환으로 articles 데이터를 다이나믹하게 처리
import { DateFormats } from '../support/enums'
describe('Global Feed', () => {
beforeEach(() => {
cy.server()
cy.route('**/api/articles**', 'fx:global_feed.json').as('getArticles')
// cy.route('**/api/articles**', ).as('getArticles')
cy.route('**/api/tags**', 'fx:popular_tags.json').as('getTags')
})
it('Should show all articles correctly', () => {
cy.visit('/')
/*
cy.fixture('global_feed.json').then(({articles})) => {
articles.forEach((article:Article, index:number) => {
cy.get('div.article-preview')
...
...
})
}
*/
// wait request and full request -> xhr
cy.wait('@getArticles').then((xhr) => {
const { articles } = xhr.responseBody as GetArticles
cy.get('div.article-preview')
.should('have.length', articles.length)
.each(($articlePreview, index) => {
const article: Article = articles[index]
cy.wrap($articlePreview)
.as('articlePreview')
.find('div.article-meta > a > img')
.should('have.attr', 'src', article.author.image)
cy.get('@articlePreview')
.find('div.article-meta > div.info > a.author')
.should('have.text', article.author.username)
// DataFormats라는 공용함수를 이용해서 Date 변환
cy.get('@articlePreview')
.find('div.article-meta > div.info > span.date')
.should(
'have.text',
Cypress.moment(article.createdAt).format(
DateFormats.ArticlePreview
)
)
cy.get('@articlePreview')
.find('div.article-meta > div.pull-xs-right > button.btn')
.should('contain', article.favoritesCount)
cy.get('@articlePreview')
.find('a.preview-link > h1')
.should('have.text', article.title)
cy.get('@articlePreview')
.find('a.preview-link > p')
.should('have.text', article.description)
/**
article.tagList.forEach((tag, tagIndex) => {
cy.get('div.article-preview')
.eq(index)
.find('a.preview-link > ul.tag-list > li.tag-default')
.eq(0)
.should('have.text','tag1')
})
*/
if (Cypress._.isEmpty(article.tagList)) {
cy.get('@articlePreview')
.find('a.preview-link > ul.tag-list')
.should('be.empty')
} else {
cy.get('@articlePreview')
.find('a.preview-link > ul.tag-list > li.tag-default')
.each(($tag, tagIndex) => {
expect($tag).to.have.text(article.tagList[tagIndex])
})
}
}).
})
})
})
import { DateFormats } from '../support/enums'
describe('Global Feed', () => {
beforeEach(() => {
cy.server()
cy.route('**/api/articles**', 'fx:global_feed.json').as('getArticles')
})
it('Should show all articles correctly', () => {
cy.visit('/')
cy.wait('@getArticles').then((xhr) => {
const { articles } = xhr.responseBody as GetArticles
/** cy.findByTestId('article-preview')
-> data-test-id={`article-preview-${article.slug}`}
-> cy.findByTestId('article-preview-${article.slug}')
**/
cy.findAllByTestId('article-preview')
.should('have.length', articles.length)
.each(($articlePreview, index) => {
const article: Article = articles[index]
cy.wrap($articlePreview).as('articlePreview')
cy.get('@articlePreview').findByRole('img')
// elememts get a role using Aria attributes
.should('have.attr', 'src', article.author.image)
// Addressed in step 3
// cy.get('@articlePreview').findByRole('link').should('exist')
cy.get('@articlePreview')
.findByText(
Cypress.moment(article.createdAt).format(
DateFormats.ArticlePreview
)
)
.should('exist')
cy.get('@articlePreview')
.findByRole('button')
.should('contain', article.favoritesCount)
cy.get('@articlePreview')
.findByRole('heading')
.should('have.text', article.title)
cy.get('@articlePreview')
.findByText(article.description)
.should('exist')
if (Cypress._.isEmpty(article.tagList)) {
cy.get('@articlePreview').findByRole('list').should('be.empty')
} else {
cy.get('@articlePreview')
.findAllByRole('listitem')
// query multiple items
.each(($tag, tagIndex) => {
expect($tag).to.have.text(article.tagList[tagIndex])
})
}
})
})
})
})
import { DateFormats } from '../support/enums'
describe('Global Feed', () => {
beforeEach(() => {
cy.server()
cy.route('**/api/articles**', 'fx:global_feed.json').as('getArticles')
})
it('Should show all articles correctly', () => {
cy.visit('/')
cy.wait('@getArticles').then((xhr) => {
const { articles } = xhr.responseBody as GetArticles
cy.findAllByTestId('article-preview')
.should('have.length', articles.length)
.each(($articlePreview, index) => {
const article: Article = articles[index]
cy.wrap($articlePreview).within(() => {
cy.findByRole('img', { name: /Article author avatar/i }).should(
'have.attr',
'src',
article.author.image
)
cy.findByRole('link', { name: article.author.username }).should(
'exist'
)
cy.findByText(
Cypress.moment(article.createdAt).format(
DateFormats.ArticlePreview
)
).should('exist')
cy.findByRole('button', { name: /Favorite article/i }).should(
'contain',
article.favoritesCount
)
cy.findByRole('heading', { name: article.title }).should('exist')
cy.findByText(article.description, { selector: 'p' }).should(
'exist'
)
cy.findByRole('list', { name: /Tags/i }).within(($tagList) => {
if (Cypress._.isEmpty(article.tagList)) {
expect($tagList).to.be.empty
} else {
cy.findAllByRole('listitem').each(($tag, tagIndex) => {
expect($tag).to.have.text(article.tagList[tagIndex])
})
}
})
})
})
})
})
})