diff --git a/e2e-tests/development-runtime/cypress.json b/e2e-tests/development-runtime/cypress.json index cd20b72e75761..48fdc722d5837 100644 --- a/e2e-tests/development-runtime/cypress.json +++ b/e2e-tests/development-runtime/cypress.json @@ -1,4 +1,5 @@ { "baseUrl": "http://localhost:8000", - "failOnStatusCode": false + "failOnStatusCode": false, + "chromeWebSecurity": false } diff --git a/e2e-tests/development-runtime/cypress/integration/functionality/restarting-prompt.js b/e2e-tests/development-runtime/cypress/integration/functionality/restarting-prompt.js new file mode 100644 index 0000000000000..ece38bdd768eb --- /dev/null +++ b/e2e-tests/development-runtime/cypress/integration/functionality/restarting-prompt.js @@ -0,0 +1,28 @@ +// NOTE(@mxstbr): while this e2e test runs and passes locally, it makes all other tests flakey for reasons I haven't been able to figure out +describe.skip(`gatsby-config.js`, () => { + beforeEach(() => { + cy.visit(`/`).waitForRouteChange() + }) + + afterEach(async () => { + cy.task(`resetGatsbyConfig`) + }) + + it(`prompts to restart when changed`, () => { + cy.on(`window:confirm`, str => { + expect(str).to.contain(`gatsby-config.js`) + + // Press "Restart" + return true + }) + + cy.task(`changeGatsbyConfig`) + cy.get(`[data-cy="restarting-screen"]`, { timeout: 10000 }).should( + `be.visible` + ) + // Restarting gatsby develop can take a while + cy.get(`[data-testid="page-component"]`, { timeout: 30000 }).should( + `be.visible` + ) + }) +}) diff --git a/e2e-tests/development-runtime/cypress/plugins/gatsby-config.js b/e2e-tests/development-runtime/cypress/plugins/gatsby-config.js new file mode 100644 index 0000000000000..658a7a5b8b04c --- /dev/null +++ b/e2e-tests/development-runtime/cypress/plugins/gatsby-config.js @@ -0,0 +1,31 @@ +const fs = require(`fs`) +const path = require(`path`) + +const CONFIG_PATH = path.join(__dirname, `../../gatsby-config.js`) +const originalConfig = fs.readFileSync(CONFIG_PATH, `utf8`) + +module.exports = { + resetGatsbyConfig: () => { + fs.writeFileSync(CONFIG_PATH, originalConfig) + return originalConfig + }, + changeGatsbyConfig: () => { + if (fs.readFileSync(CONFIG_PATH, `utf8`) !== originalConfig) + throw new Error( + `It looks like the gatsby-config.js has already been changed. Please call cy.task('resetGatsbyConfig') first.` + ) + // Switch the order of two plugins around to trigger a restart + // that doesn't affect the functionality of the site. + const changed = originalConfig.replace( + `\`gatsby-source-fake-data\`, + \`gatsby-transformer-sharp\`, +`, + ` + \`gatsby-transformer-sharp\`, + \`gatsby-source-fake-data\`,` + ) + + fs.writeFileSync(CONFIG_PATH, changed) + return changed + }, +} diff --git a/e2e-tests/development-runtime/cypress/plugins/index.js b/e2e-tests/development-runtime/cypress/plugins/index.js index 793d37b2dc7d7..035b15de976eb 100644 --- a/e2e-tests/development-runtime/cypress/plugins/index.js +++ b/e2e-tests/development-runtime/cypress/plugins/index.js @@ -11,9 +11,13 @@ // This function is called when a project is opened or re-opened (e.g. due to // the project's config changing) const blockResources = require(`./block-resources`) +const gatsbyConfig = require(`./gatsby-config`) module.exports = (on, config) => { // `on` is used to hook into various events Cypress emits // `config` is the resolved Cypress config - on(`task`, Object.assign({}, blockResources)) + on(`task`, { + ...blockResources, + ...gatsbyConfig, + }) } diff --git a/packages/gatsby/cache-dir/app.js b/packages/gatsby/cache-dir/app.js index e0f8dd419487f..ac3cbcf13a095 100644 --- a/packages/gatsby/cache-dir/app.js +++ b/packages/gatsby/cache-dir/app.js @@ -34,23 +34,35 @@ apiRunnerAsync(`onClientEntry`).then(() => { .then(res => res.json()) .then(services => { if (services.developstatusserver) { + let isRestarting = false const parentSocket = io( `http://${window.location.hostname}:${services.developstatusserver.port}` ) - parentSocket.on(`develop:needs-restart`, msg => { + parentSocket.on(`structured-log`, msg => { if ( + !isRestarting && + msg.type === `LOG_ACTION` && + msg.action.type === `DEVELOP` && + msg.action.payload === `RESTART_REQUIRED` && window.confirm( - `The develop process needs to be restarted for the changes to ${msg.dirtyFile} to be applied.\nDo you want to restart the develop process now?` + `The develop process needs to be restarted for the changes to ${msg.action.dirtyFile} to be applied.\nDo you want to restart the develop process now?` ) ) { - parentSocket.once(`develop:is-starting`, msg => { + isRestarting = true + parentSocket.emit(`develop:restart`, () => { window.location.reload() }) - parentSocket.once(`develop:started`, msg => { - window.location.reload() - }) - parentSocket.emit(`develop:restart`) + } + + if ( + isRestarting && + msg.type === `LOG_ACTION` && + msg.action.type === `SET_STATUS` && + msg.action.payload === `SUCCESS` + ) { + isRestarting = false + window.location.reload() } }) diff --git a/packages/gatsby/src/commands/develop.ts b/packages/gatsby/src/commands/develop.ts index b7744da95ef5b..da73d4c1a120a 100644 --- a/packages/gatsby/src/commands/develop.ts +++ b/packages/gatsby/src/commands/develop.ts @@ -342,21 +342,23 @@ module.exports = async (program: IProgram): Promise => { process.send(msg) } + io.emit(`structured-log`, msg) + if ( msg.type === `LOG_ACTION` && msg.action.type === `SET_STATUS` && msg.action.payload === `SUCCESS` ) { proxy.serveSite() - io.emit(`develop:started`) } } io.on(`connection`, socket => { - socket.on(`develop:restart`, async () => { + socket.on(`develop:restart`, async respond => { isRestarting = true proxy.serveRestartingScreen() - io.emit(`develop:is-starting`) + // respond() responds to the client, which in our case prompts it to reload the page to show the restarting screen + if (respond) respond(`develop:is-starting`) await developProcess.stop() developProcess.start() developProcess.onMessage(handleChildProcessIPC) @@ -420,8 +422,13 @@ module.exports = async (program: IProgram): Promise => { console.warn( `develop process needs to be restarted to apply the changes to ${file}` ) - io.emit(`develop:needs-restart`, { - dirtyFile: file, + io.emit(`structured-log`, { + type: `LOG_ACTION`, + action: { + type: `DEVELOP`, + payload: `RESTART_REQUIRED`, + dirtyFile: file, + }, }) }) } diff --git a/packages/gatsby/src/utils/restarting-screen.ts b/packages/gatsby/src/utils/restarting-screen.ts index 1d1619bcb75db..e42095a351b34 100644 --- a/packages/gatsby/src/utils/restarting-screen.ts +++ b/packages/gatsby/src/utils/restarting-screen.ts @@ -69,7 +69,7 @@ export default html` } - +
{ - window.location.reload() - }) + socket.on("structured-log", msg => { + if (msg.type !== "LOG_ACTION") return + + if ( + msg.action.type === "SET_STATUS" && + msg.action.payload === "SUCCESS" + ) { + window.location.reload() + } - socket.on("develop:needs-restart", () => { - socket.emit("develop:restart") + if ( + msg.action.type === "DEVELOP" && + msg.action.payload === "RESTART_REQUIRED" + ) { + socket.emit("develop:restart") + } }) socket.on("disconnect", () => {