diff --git a/packages/core/config/webpack.common.js b/packages/core/config/webpack.common.js index 2c99aac5ad97..8605f7c0139c 100644 --- a/packages/core/config/webpack.common.js +++ b/packages/core/config/webpack.common.js @@ -341,6 +341,11 @@ module.exports = (webpackEnv) => { filename: 'static/media/[name].[contenthash:8][ext]', }, }, + // (8) + !redwoodConfig.experimental.realtime.enabled && { + test: require.resolve('@redwoodjs/web/dist/apollo/sseLink'), + use: require.resolve('null-loader'), + }, ].filter(Boolean), }, ], diff --git a/packages/project-config/src/__tests__/config.test.ts b/packages/project-config/src/__tests__/config.test.ts index 031840465bd4..face91a6d8d4 100644 --- a/packages/project-config/src/__tests__/config.test.ts +++ b/packages/project-config/src/__tests__/config.test.ts @@ -59,6 +59,9 @@ describe('getConfig', () => { "enabled": false, "wrapApi": true, }, + "realtime": { + "enabled": false, + }, "studio": { "graphiql": { "authImpersonation": { diff --git a/packages/project-config/src/config.ts b/packages/project-config/src/config.ts index b99fe315057e..16af8f4d32a6 100644 --- a/packages/project-config/src/config.ts +++ b/packages/project-config/src/config.ts @@ -105,6 +105,9 @@ export interface Config { plugins: CLIPlugin[] } useSDLCodeGenForGraphQLTypes: boolean + realtime: { + enabled: boolean + } } } @@ -181,6 +184,9 @@ const DEFAULT_CONFIG: Config = { ], }, useSDLCodeGenForGraphQLTypes: false, + realtime: { + enabled: false, + }, }, } diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts index 92d6898cdd9d..f5377a39d73c 100644 --- a/packages/vite/src/index.ts +++ b/packages/vite/src/index.ts @@ -265,6 +265,12 @@ export default function redwoodPluginVite(): PluginOption[] { id: /@redwoodjs\/router\/dist\/splash-page/, }, ]), + !rwConfig.experimental.realtime.enabled && + removeFromBundle([ + { + id: /@redwoodjs\/web\/dist\/apollo\/sseLink/, + }, + ]), react({ babel: { ...getWebSideDefaultBabelConfig({ diff --git a/packages/vite/src/plugins/vite-plugin-remove-from-bundle.ts b/packages/vite/src/plugins/vite-plugin-remove-from-bundle.ts index 3cfd18d8a138..5292ecc89057 100644 --- a/packages/vite/src/plugins/vite-plugin-remove-from-bundle.ts +++ b/packages/vite/src/plugins/vite-plugin-remove-from-bundle.ts @@ -31,7 +31,7 @@ export default function removeFromBundle( // Currently configured for CJS only. const EMPTY_MODULE = { - code: `module.exports = null`, + code: `module.exports = {}`, } export function excludeOnMatch(modulesToExclude: ModulesToExclude, id: string) { diff --git a/packages/web/src/apollo/index.tsx b/packages/web/src/apollo/index.tsx index 61faddb51dfc..d696a2b2e277 100644 --- a/packages/web/src/apollo/index.tsx +++ b/packages/web/src/apollo/index.tsx @@ -190,23 +190,26 @@ const ApolloProviderWithFetchConfig: React.FunctionComponent<{ // Our terminating link needs to be smart enough to handle subscriptions, and if the GraphQL query // is subscription it needs to use the SSELink (server sent events link). - const httpOrSSELink = apolloClient.split( - ({ query }) => { - const definition = getMainDefinition(query) - - return ( - definition.kind === 'OperationDefinition' && - definition.operation === 'subscription' - ) - }, - new SSELink({ - url: uri, - auth: { authProviderType, tokenFn: getToken }, - httpLinkConfig, - headers, - }), - httpLink - ) + const httpOrSSELink = + typeof SSELink !== 'undefined' + ? apolloClient.split( + ({ query }) => { + const definition = getMainDefinition(query) + + return ( + definition.kind === 'OperationDefinition' && + definition.operation === 'subscription' + ) + }, + new SSELink({ + url: uri, + auth: { authProviderType, tokenFn: getToken }, + httpLinkConfig, + headers, + }), + httpLink + ) + : httpLink // The order here is important. The last link *must* be a terminating link like HttpLink or SSELink. const redwoodApolloLinks: RedwoodApolloLinks = [ diff --git a/tasks/smoke-tests/common.ts b/tasks/smoke-tests/common.ts index b7ca0b781eae..37af02c4696a 100644 --- a/tasks/smoke-tests/common.ts +++ b/tasks/smoke-tests/common.ts @@ -3,43 +3,50 @@ import { expect, PlaywrightTestArgs } from '@playwright/test' export async function smokeTest({ page }: PlaywrightTestArgs) { await page.goto('/') - // Check that the blog posts are being loaded. - // Avoid checking titles because we edit them in other tests. - await page.textContent('text=Meh waistcoat succulents umami') - await page.textContent('text=Raclette shoreditch before they sold out lyft.') - await page.textContent( - 'text=baby single- origin coffee kickstarter lo - fi paleo skateboard.' - ) - + // Check that the blog posts load. We're deliberately not checking their titles because we edit them in other tests. + await expect( + page.getByText( + 'Meh waistcoat succulents umami asymmetrical, hoodie post-ironic paleo chillwave ' + ) + ).toBeVisible() + await expect( + page.getByText( + 'Raclette shoreditch before they sold out lyft. Ethical bicycle rights meh prism ' + ) + ).toBeVisible() + await expect( + page.getByText( + "I'm baby single- origin coffee kickstarter lo - fi paleo skateboard.Tumblr hasht" + ) + ).toBeVisible() + + // CSS checks. We saw this break when we switched bundlers, so while it's not comprehensive, it's at least something. + // While playwright recommends against using locators that are too-closely tied to the DOM, `#redwood-app` is a stable ID. const bgBlue700 = 'rgb(29, 78, 216)' - expect( - await page - .locator('#redwood-app > header') - .evaluate((e) => window.getComputedStyle(e).backgroundColor) - ).toBe(bgBlue700) + expect(page.locator('#redwood-app > header')).toHaveCSS( + 'background-color', + bgBlue700 + ) const textBlue400 = 'rgb(96, 165, 250)' - expect( - await page - .locator('header a') - .filter({ hasText: 'Redwood Blog' }) - .evaluate((e) => window.getComputedStyle(e).color) - ).toBe(textBlue400) - - // Click text=About - await page.click('text=About') + expect(await page.getByRole('link', { name: 'Redwood Blog' })).toHaveCSS( + 'color', + textBlue400 + ) + // Check the about page. + await page.getByRole('link', { name: 'About', exact: true }).click() expect(page.url()).toBe('http://localhost:8910/about') - - await page.textContent( - 'text=This site was created to demonstrate my mastery of Redwood: Look on my works, ye' + await page.getByText( + 'This site was created to demonstrate my mastery of Redwood: Look on my works, ye' ) - // Click text=Contact - await page.click('text=Contact') + + // Check the contact us page. + await page.getByRole('link', { name: 'Contact Us' }).click() expect(page.url()).toBe('http://localhost:8910/contact') - // Click text=Admin - await page.click('text=Admin') + // Check the admin page. + await page.getByRole('link', { name: 'Admin' }).click() expect(page.url()).toBe('http://localhost:8910/posts') }