Skip to content

Commit

Permalink
test: e2e tests for Slices API (#36746)
Browse files Browse the repository at this point in the history
* initial

* update gatsby node

* temporary: use latest slices canary

* import slicesData

* use next

* fix slices undefined issue

* put staticqueryresult provider above wrapRootElement when rendering slice html

* don't output UI in wrapRootElement

* move wrapRootElement UI markup to wrapPageElement, keep static query in wrapRootElement

* render wrapRootElement static query tester component on every page, but move rendering to wrapPageElement

* move wrapRootElement test markup to separate component

* don't fail on hydration errors in slices test in offline mode

Co-authored-by: Ty Hopp <tyhopp@users.noreply.github.com>
Co-authored-by: pieh <misiek.piechowiak@gmail.com>
  • Loading branch information
3 people committed Nov 16, 2022
1 parent 9d67c13 commit f158930
Show file tree
Hide file tree
Showing 25 changed files with 525 additions and 63 deletions.
@@ -0,0 +1,25 @@
import { allRecipes, allRecipeAuthors } from "../../../shared-data/slices"

/**
* Test behaviour when a slice is created and passed `slices` option to createPage
*/

describe("Slice passed via createPage", () => {
it("Pages created with slices mapping have correct content", () => {
allRecipes.forEach(recipe => {
cy.visit(`recipe/${recipe.id}`).waitForRouteChange()

cy.getTestElement(`recipe-name`)
.invoke(`text`)
.should(`contain`, recipe.name)

cy.getTestElement(`recipe-description`)
.invoke(`text`)
.should(`contain`, recipe.description)

cy.getTestElement(`recipe-author-name`)
.invoke(`text`)
.should(`contain`, allRecipeAuthors.find(author => recipe.authorId === author.id).name)
})
})
})
33 changes: 33 additions & 0 deletions e2e-tests/development-runtime/cypress/integration/slices/slices.js
@@ -0,0 +1,33 @@
/**
* Test basic Slices API behaviour like context, props, ....
*/

describe(`Slices`, () => {
beforeEach(() => {
cy.visit(`/`).waitForRouteChange()
})

it(`Slice content show on screen`, () => {
cy.getTestElement(`footer-static-text`)
.invoke(`text`)
.should(`contain`, `Built with`)
})

it(`Slice recieves context passed via createSlice`, () => {
cy.getTestElement(`footer-slice-context-value`)
.invoke(`text`)
.should(`contain`, `Gatsby`)
})

it(`Slice can take in props`, () => {
cy.getTestElement(`footer-props`)
.invoke(`text`)
.should(`contains`, `Gatsbyjs`)
})

it(`Slice can consume a context wrapped in WrapRootElement`, () => {
cy.getTestElement(`footer-context-derieved-value`)
.invoke(`text`)
.should(`contain`, `2`)
})
})
47 changes: 43 additions & 4 deletions e2e-tests/development-runtime/gatsby-node.js
@@ -1,6 +1,7 @@
const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)
const headFunctionExportSharedData = require("./shared-data/head-function-export")
const slicesData = require("./shared-data/slices")
const {
addRemoteFilePolyfillInterface,
polyfillImageServiceDevRoutes,
Expand Down Expand Up @@ -37,7 +38,8 @@ exports.sourceNodes = ({ actions, createNodeId, createContentDigest }) => {
const items = [
{
name: "photoA.jpg",
url: "https://images.unsplash.com/photo-1517849845537-4d257902454a?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2000&q=80",
url:
"https://images.unsplash.com/photo-1517849845537-4d257902454a?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2000&q=80",
placeholderUrl:
"https://images.unsplash.com/photo-1517849845537-4d257902454a?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=%width%&h=%height%",
mimeType: "image/jpg",
Expand All @@ -47,15 +49,17 @@ exports.sourceNodes = ({ actions, createNodeId, createContentDigest }) => {
},
{
name: "photoB.jpg",
url: "https://images.unsplash.com/photo-1552053831-71594a27632d?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&h=2000&q=10",
url:
"https://images.unsplash.com/photo-1552053831-71594a27632d?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&h=2000&q=10",
mimeType: "image/jpg",
filename: "photo-1552053831.jpg",
width: 1247,
height: 2000,
},
{
name: "photoC.jpg",
url: "https://images.unsplash.com/photo-1561037404-61cd46aa615b?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2000&q=80",
url:
"https://images.unsplash.com/photo-1561037404-61cd46aa615b?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2000&q=80",
placeholderUrl:
"https://images.unsplash.com/photo-1561037404-61cd46aa615b?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=%width%&h=%height%",
mimeType: "image/jpg",
Expand Down Expand Up @@ -122,7 +126,7 @@ exports.onCreateNode = function onCreateNode({
* @type {import('gatsby').createPages}
*/
exports.createPages = async function createPages({
actions: { createPage, createRedirect },
actions: { createPage, createRedirect, createSlice },
graphql,
}) {
const { data } = await graphql(`
Expand Down Expand Up @@ -172,6 +176,41 @@ exports.createPages = async function createPages({
})
})

//-------------------------Slice API----------------------------
createSlice({
id: `footer`,
component: path.resolve(`./src/components/footer.js`),
context: {
framework: slicesData.framework,
},
})

slicesData.allRecipeAuthors.forEach(({ id, name }) => {
createSlice({
id: `author-${id}`,
component: path.resolve(`./src/components/recipe-author.js`),
context: {
name,
id,
},
})
})

slicesData.allRecipes.forEach(({ authorId, id, name, description }) => {
createPage({
path: `/recipe/${id}`,
component: path.resolve(`./src/templates/recipe.js`),
context: {
description: description,
name,
},
slices: {
author: `author-${authorId}`,
},
})
})
//---------------------------------------------------------------

createPage({
path: `/안녕`,
component: path.resolve(`src/pages/page-2.js`),
Expand Down
25 changes: 25 additions & 0 deletions e2e-tests/development-runtime/shared-data/slices.js
@@ -0,0 +1,25 @@
const allRecipes = [
{
id: "r1",
name: "Jollof Rice",
description:
"It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).",
authorId: "a-1",
},
{
id: "r2",
name: "Ewa Agoyin",
description:
"It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).",
authorId: "a-2",
},
]

const allRecipeAuthors = [
{ id: "a-1", name: "Jude" },
{ id: "a-2", name: "Ty" },
]

const framework = "Gatsby"

module.exports = { allRecipes, allRecipeAuthors, framework }
23 changes: 23 additions & 0 deletions e2e-tests/development-runtime/src/components/footer.js
@@ -0,0 +1,23 @@
import React, { useContext } from "react"
import { ContextForSlices } from "../context-for-slices"

// Use as a Slice
function Footer({ framework, lang, sliceContext: { framework: frameworkViaContext }}) {
const { posts } = useContext(ContextForSlices)

return (
<footer
style={{
marginTop: `10px`,
fontSize: `12px`,
}}
>
<span data-testid="footer-slice-context-value">{frameworkViaContext}</span>
<span data-testid="footer-static-text">Built with {` `}</span>
<span data-testid="footer-props">{`${framework}${lang}`}</span>
{` `}Posts Count: <span data-testid="footer-context-derieved-value">{`${posts.length}`}</span>
</footer>
)
}

export default Footer
8 changes: 7 additions & 1 deletion e2e-tests/development-runtime/src/components/layout.js
@@ -1,6 +1,6 @@
import React from "react"
import PropTypes from "prop-types"
import { StaticQuery, graphql } from "gatsby"
import { StaticQuery, graphql, Slice } from "gatsby"

import Header from "./header"
import "./layout.css"
Expand Down Expand Up @@ -29,6 +29,12 @@ const Layout = ({ children }) => (
>
{children}
</div>
<Slice alias="footer" framework="Gatsby" lang="js"/>

{/** The slice below doesn't exist but it shouldn't break build */}
<Slice alias="this-alias-does-not-exist" allowEmpty/>

{/** Insert this here and expect it to fail <Slice alias="this-alias-does-not-exist-too"/>*/}
</>
)}
/>
Expand Down
15 changes: 15 additions & 0 deletions e2e-tests/development-runtime/src/components/recipe-author.js
@@ -0,0 +1,15 @@
import React from "react"

// Use as a Slice
function RecipeAuthor({ sliceContext: { name } }) {
return (
<div>
Written by{" "}
<span data-testid="recipe-author-name" style={{ fontWeight: "bold" }}>
{name}
</span>
</div>
)
}

export default RecipeAuthor
25 changes: 25 additions & 0 deletions e2e-tests/development-runtime/src/context-for-slices.js
@@ -0,0 +1,25 @@
import React from "react"

const ContextForSlices = React.createContext()

const ContextForSlicesProvider = ({ children }) => {
const contextValue = {
posts: [
{
title: "My first blog post",
content: "This is my first blog post",
},
{
title: "My second blog post",
content: "This is my second blog post",
},
],
}
return (
<ContextForSlices.Provider value={contextValue}>
{children}
</ContextForSlices.Provider>
)
}

export { ContextForSlices, ContextForSlicesProvider }
15 changes: 15 additions & 0 deletions e2e-tests/development-runtime/src/templates/recipe.js
@@ -0,0 +1,15 @@
import React from "react"
import { Slice } from "gatsby"
import Layout from "../components/layout"

const Recipe = ({ pageContext: { description, name } }) => {
return (
<Layout>
<h1 data-testid="recipe-name">{name}</h1>
<p data-testid="recipe-description">{description}</p>
<Slice alias="author" />
</Layout>
)
}

export default Recipe
5 changes: 3 additions & 2 deletions e2e-tests/development-runtime/src/wrap-root-element.js
@@ -1,6 +1,7 @@
import React from "react"
import { StaticQuery, graphql, Script } from "gatsby"
import { scripts } from "../gatsby-script-scripts"
import { ContextForSlicesProvider } from "./context-for-slices"

const WrapRootElement = ({ element }) => (
<StaticQuery
Expand All @@ -18,7 +19,7 @@ const WrapRootElement = ({ element }) => (
siteMetadata: { title },
},
}) => (
<>
<ContextForSlicesProvider>
{element}
<Script src={scripts.jQuery} strategy="post-hydrate" />
<Script src={scripts.popper} strategy="idle" />
Expand All @@ -29,7 +30,7 @@ const WrapRootElement = ({ element }) => (
%TEST_HMR_IN_GATSBY_BROWSER%
</div>
</div>
</>
</ContextForSlicesProvider>
)}
/>
)
Expand Down
@@ -0,0 +1,25 @@
import { allRecipes, allRecipeAuthors } from "../../../shared-data/slices"

/**
* Test behaviour when a slice is created and passed `slices` option to createPage
*/

describe("Slice passed via createPage", () => {
it("Pages created with slices mapping have correct content", () => {
allRecipes.forEach(recipe => {
cy.visit(`recipe/${recipe.id}`).waitForRouteChange()

cy.getTestElement(`recipe-name`)
.invoke(`text`)
.should(`contain`, recipe.name)

cy.getTestElement(`recipe-description`)
.invoke(`text`)
.should(`contain`, recipe.description)

cy.getTestElement(`recipe-author-name`)
.invoke(`text`)
.should(`contain`, allRecipeAuthors.find(author => recipe.authorId === author.id).name)
})
})
})
44 changes: 44 additions & 0 deletions e2e-tests/production-runtime/cypress/integration/slices/slices.js
@@ -0,0 +1,44 @@
/**
* Test basic Slices API behaviour like context, props, ....
*/

Cypress.on(`uncaught:exception`, err => {
if (
(err.message.includes(`Minified React error #418`) ||
err.message.includes(`Minified React error #423`) ||
err.message.includes(`Minified React error #425`)) &&
Cypress.env(`TEST_PLUGIN_OFFLINE`)
) {
return false
}
})

describe(`Slices`, () => {
beforeEach(() => {
cy.visit(`/`).waitForRouteChange()
})

it(`Slice content show on screen`, () => {
cy.getTestElement(`footer-static-text`)
.invoke(`text`)
.should(`contain`, `Built with`)
})

it(`Slice recieves context passed via createSlice`, () => {
cy.getTestElement(`footer-slice-context-value`)
.invoke(`text`)
.should(`contain`, `Gatsby`)
})

it(`Slice can take in props`, () => {
cy.getTestElement(`footer-props`)
.invoke(`text`)
.should(`contains`, `Gatsbyjs`)
})

it(`Slice can consume a context wrapped in WrapRootElement`, () => {
cy.getTestElement(`footer-context-derieved-value`)
.invoke(`text`)
.should(`contain`, `2`)
})
})

0 comments on commit f158930

Please sign in to comment.