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

test(e2e-development-runtime): add tests for loading indicator behavior #28700

Merged
merged 5 commits into from
Feb 23, 2021
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
function toggleLoadingIndicator(command) {
cy.then(() => fetch(`/___loading-indicator/${command}/`))
cy.wait(5000)
}

const refresh = async payload => {
return await fetch(`/__refresh/gatsby-source-query-on-demand-data/`, {
method: `POST`,
headers: {
"Content-Type": `application/json`,
},
body: payload ? JSON.stringify({ updateData: payload }) : undefined,
})
}

function assertIndicatorExists(doesExists) {
cy.get(`[data-gatsby-loading-indicator="root"]`, { timeout: 10000 }).should(
doesExists ? `exist` : `not.exist`
)
}

function assertIndicatorVisible(isVisible) {
cy.get(`[data-gatsby-loading-indicator="root"]`, { timeout: 10000 }).should(
isVisible ? `be.visible` : `not.be.visible`
)

cy.screenshot()
}

function runTests({
data,
sleep,
mode,
expectIndicatorEnabled,
expectIndicatorVisible,
}) {
// make sure we are not on a page with query
cy.visit(`/query-on-demand/no-query/`).waitForRouteChange()

// setup data for page with query
cy.then(() =>
refresh({
data,
sleep,
})
)

if (mode === `first-render`) {
cy.visit(`/query-on-demand/possibly-heavy-query/`)
} else if (mode === `client-navigation`) {
cy.findByTestId(`heavy-query-page-link`).click()
} else {
throw new Error(
`Unknown mode - should be one of ["first-render", "client-navigation"]`
)
}

// we wait for first render or for client side navigation to complete when we expect for loading indicator
// to show up (depending on test setup)
if (expectIndicatorEnabled) {
assertIndicatorExists(true)
assertIndicatorVisible(expectIndicatorVisible)
} else {
assertIndicatorExists(false)
}

cy.waitForRouteChange()

// once we finished first render or client side navigation - loading indicator should not be visible anymore (if it was before)
if (expectIndicatorEnabled) {
assertIndicatorExists(true)
assertIndicatorVisible(false)
} else {
assertIndicatorExists(false)
}

// making sure our test setup was correct and expect query result is rendered
cy.findByTestId(`query-data`).should(`have.text`, data)
}

describe(`Loading indicator`, () => {
before(async () => {
Cypress.config(`includeShadowDom`, true)
})

after(async () => {
toggleLoadingIndicator(`auto`)
})

// we disable showing loading indicator in cypress by default
// to not impact user tests - this is to make sure that's the case/catch regressions
describe(`Defaults in cypress env (doesn't show indicator)`, () => {
before(async () => {
toggleLoadingIndicator(`auto`)
})

describe(`Page with light/quick query doesn't show indicator`, () => {
const config = {
sleep: 0,
expectIndicatorEnabled: false,
expectIndicatorVisible: `doesn't matter because indicator should not be enabled`,
}

it(`Initial first render`, () => {
runTests({
...config,
data: `quick-first-render-with-disabled-indicator`,
mode: `first-render`,
})
})

it(`On navigation`, () => {
runTests({
...config,
data: `quick-client-navigation-with-disabled-indicator`,
mode: `client-navigation`,
})
})
})

describe(`Page with heavy/long running query doesn't show indicator (indicator is disabled)`, () => {
const config = {
sleep: 5000,
expectIndicatorEnabled: false,
expectIndicatorVisible: `doesn't matter because indicator should not be enabled`,
}

it(`Initial first render`, () => {
runTests({
...config,
data: `slow-first-render-with-disabled-indicator`,
mode: `first-render`,
})
})

it(`On navigation`, () => {
runTests({
...config,
data: `slow-client-navigation-with-disabled-indicator`,
mode: `client-navigation`,
})
})
})
})

describe(`With enabled loading indicator`, () => {
before(async () => {
toggleLoadingIndicator(`enable`)
})

describe(`Page with light/quick query doesn't show indicator`, () => {
const config = {
sleep: 0,
expectIndicatorEnabled: true,
expectIndicatorVisible: false,
}

it(`Initial first render`, () => {
runTests({
...config,
data: `quick-first-render-with-enabled-indicator`,
mode: `first-render`,
})
})

it(`On navigation`, () => {
runTests({
...config,
data: `quick-client-navigation-with-enabled-indicator`,
mode: `client-navigation`,
})
})
})

describe(`Page with heavy/long running query shows indicator`, () => {
const config = {
sleep: 5000,
expectIndicatorEnabled: true,
expectIndicatorVisible: true,
}

it(`Initial first render`, () => {
runTests({
...config,
data: `slow-first-render-with-enabled-indicator`,
mode: `first-render`,
})
})

it(`On navigation`, () => {
runTests({
...config,
data: `slow-client-navigation-with-enabled-indicator`,
mode: `client-navigation`,
})
})
})
})
})
1 change: 1 addition & 0 deletions e2e-tests/development-runtime/gatsby-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ module.exports = {
},
`gatsby-source-fake-data`,
`gatsby-source-pinc-data`,
`gatsby-source-query-on-demand-data`,
`gatsby-transformer-sharp`,
`gatsby-transformer-json`,
{
Expand Down
2 changes: 1 addition & 1 deletion e2e-tests/development-runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"license": "MIT",
"scripts": {
"build": "gatsby build",
"develop": "cross-env CYPRESS_SUPPORT=y ENABLE_GATSBY_REFRESH_ENDPOINT=true gatsby develop",
"develop": "cross-env CYPRESS_SUPPORT=y ENABLE_GATSBY_REFRESH_ENDPOINT=true GATSBY_EXPERIMENTAL_QUERY_ON_DEMAND=y gatsby develop",
"serve": "gatsby serve",
"start": "npm run develop",
"format": "prettier --write \"src/**/*.js\"",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
const initialData = {
data: `initial`,
sleep: 0,
}

exports.sourceNodes = async ({
actions,
createNodeId,
createContentDigest,
webhookBody,
}) => {
const { createNode } = actions

function doCreateNode(data) {
const id = `query-on-demand-testing-node`

const node = {
...data,
id: createNodeId(id),
selector: id,
internal: {
contentDigest: createContentDigest(data),
type: `QueryOnDemandMock`,
},
}
return createNode(node)
}

if (webhookBody && webhookBody.updateData) {
console.log(
`[query-on-demand-source] Received webhook data`,
webhookBody.updateData
)
/*
expected shape of webhook body:
{
"updateData": {
sleep: <time-query-will-take-to-resolve-in-milliseconds>,
data "<some-text-to-that-will-be-rendered-to-assert-update-did-happen>"
}
}
*/

let dataAfterSync = {
...initialData,
}
if (webhookBody.updateData) {
dataAfterSync = {
...dataAfterSync,
...webhookBody.updateData,
}
}

doCreateNode(dataAfterSync)
} else {
doCreateNode(initialData)
}
}

exports.createResolvers = ({ createResolvers }) => {
createResolvers({
QueryOnDemandMock: {
doSomeWaiting: {
type: `String`,
resolve: source => {
return new Promise(resolve => {
setTimeout(() => {
resolve(source.data)
}, source.sleep)
})
},
},
},
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from "react"
import { Link } from "gatsby"

export default function QODNoQuery() {
return (
<>
<Link to="../possibly-heavy-query/" data-testid="heavy-query-page-link">
Page with heavy query
</Link>
<p>Hello on a page without a query</p>
</>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from "react"
import { Link, graphql } from "gatsby"

export default function QODHeavyQuery({ data }) {
return (
<>
<Link to="../no-query/" data-testid="no-query-page-link">
Page with no query
</Link>
<p>Hello on a page with possibly heavy query</p>
<p>
Data:{" "}
<code data-testid="query-data">
{data.queryOnDemandMock.doSomeWaiting}
</code>
</p>
</>
)
}

export const query = graphql`
{
queryOnDemandMock {
doSomeWaiting
}
}
`
2 changes: 1 addition & 1 deletion packages/gatsby/src/services/initialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ export async function initialize({
// we don't want to ever have this flag enabled for anything than develop
// in case someone have this env var globally set
delete process.env.GATSBY_EXPERIMENTAL_QUERY_ON_DEMAND
} else if (isCI()) {
} else if (isCI() && !process.env.CYPRESS_SUPPORT) {
delete process.env.GATSBY_EXPERIMENTAL_QUERY_ON_DEMAND
reporter.verbose(
`Experimental Query on Demand feature is not available in CI environment. Continuing with eager query running.`
Expand Down
18 changes: 13 additions & 5 deletions packages/gatsby/src/utils/loading-indicator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ import { writeModule } from "./gatsby-webpack-virtual-modules"
// to not cause problems for users when they iterate on their E2E tests
// this check could be expanded in the future to add support for more scenarios
// like that.
let indicatorEnabled: "initial" | true | false | undefined = undefined
let indicatorEnabled: "auto" | true | false | undefined = undefined

export function writeVirtualLoadingIndicatorModule(): void {
if (indicatorEnabled === undefined) {
indicatorEnabled =
process.env.GATSBY_EXPERIMENTAL_QUERY_ON_DEMAND &&
process.env.GATSBY_QUERY_ON_DEMAND_LOADING_INDICATOR === `true`
? `initial`
? `auto`
: false
}

Expand All @@ -25,7 +25,7 @@ export function writeVirtualLoadingIndicatorModule(): void {
`
export function isLoadingIndicatorEnabled() {
return ${
indicatorEnabled === `initial`
indicatorEnabled === `auto`
? `\`Cypress\` in window
? false
: true`
Expand All @@ -43,10 +43,18 @@ export function routeLoadingIndicatorRequests(app: Express): void {
} else if (req.params.method === `disable` && indicatorEnabled !== false) {
indicatorEnabled = false
writeVirtualLoadingIndicatorModule()
} else if (req.params.method === `auto` && indicatorEnabled !== `auto`) {
indicatorEnabled = `auto`
writeVirtualLoadingIndicatorModule()
}

res.send({
status: indicatorEnabled ? `enabled` : `disabled`,
res.status(200).send({
status:
indicatorEnabled === `auto`
? `auto`
: indicatorEnabled
? `enabled`
: `disabled`,
})
})
}