Skip to content

Commit 43059d7

Browse files
authored
Merge pull request #5198 from Shopify/01-15-don_t_show_dev_shorcuts_until_dev_session_is_ready
Don't show dev shortcuts until dev session is ready
2 parents 31ee2c2 + 7b5b5a1 commit 43059d7

File tree

3 files changed

+111
-7
lines changed

3 files changed

+111
-7
lines changed

packages/app/src/cli/services/dev/processes/dev-session.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ let bundleControllers: AbortController[] = []
5959
// Since the watcher can emit events before the dev session is ready, we need to keep track of the status
6060
let isDevSessionReady = false
6161

62+
export function devSessionStatus() {
63+
return {
64+
isDevSessionReady,
65+
}
66+
}
67+
6268
export async function setupDevSessionProcess({
6369
app,
6470
apiKey,

packages/app/src/cli/services/dev/ui/components/Dev.test.tsx

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {calculatePrefixColumnSize, Dev} from './Dev.js'
22
import {fetchAppPreviewMode} from '../../fetch.js'
33
import {testDeveloperPlatformClient, testUIExtension} from '../../../../models/app/app.test-data.js'
4+
import {devSessionStatus} from '../../processes/dev-session.js'
45
import {
56
getLastFrameAfterUnmount,
67
render,
@@ -12,14 +13,15 @@ import {
1213
} from '@shopify/cli-kit/node/testing/ui'
1314
import {AbortController, AbortSignal} from '@shopify/cli-kit/node/abort'
1415
import React from 'react'
15-
import {describe, expect, test, vi} from 'vitest'
16+
import {describe, expect, test, vi, beforeEach} from 'vitest'
1617
import {unstyled} from '@shopify/cli-kit/node/output'
1718
import {openURL} from '@shopify/cli-kit/node/system'
1819
import {Writable} from 'stream'
1920

2021
vi.mock('@shopify/cli-kit/node/system')
2122
vi.mock('../../../context.js')
2223
vi.mock('../../fetch.js')
24+
vi.mock('../../processes/dev-session.js')
2325

2426
const developerPlatformClient = testDeveloperPlatformClient()
2527

@@ -40,6 +42,10 @@ const developerPreview = {
4042
}
4143

4244
describe('Dev', () => {
45+
beforeEach(() => {
46+
vi.mocked(devSessionStatus).mockReturnValue({isDevSessionReady: true})
47+
})
48+
4349
test('renders a stream of concurrent outputs from sub-processes, shortcuts and a preview url', async () => {
4450
// Given
4551
let backendPromiseResolve: () => void
@@ -968,6 +974,66 @@ describe('Dev', () => {
968974
// unmount so that polling is cleared after every test
969975
renderInstance.unmount()
970976
})
977+
978+
test('updates UI when devSessionEnabled changes from false to true', async () => {
979+
// Given
980+
vi.mocked(devSessionStatus).mockReturnValue({isDevSessionReady: false})
981+
982+
const renderInstance = render(
983+
<Dev
984+
processes={[]}
985+
abortController={new AbortController()}
986+
previewUrl="https://shopify.com"
987+
graphiqlUrl="https://graphiql.shopify.com"
988+
graphiqlPort={1234}
989+
app={{
990+
...testApp,
991+
canEnablePreviewMode: false,
992+
developerPlatformClient: {
993+
...testDeveloperPlatformClient(),
994+
supportsDevSessions: true,
995+
},
996+
}}
997+
developerPreview={developerPreview}
998+
shopFqdn="mystore.shopify.io"
999+
/>,
1000+
)
1001+
1002+
// Initial state - dev session not ready
1003+
expect(unstyled(renderInstance.lastFrame()!).replace(/\d/g, '0')).toMatchInlineSnapshot(`
1004+
"
1005+
────────────────────────────────────────────────────────────────────────────────────────────────────
1006+
1007+
› Press q │ quit
1008+
1009+
Preview URL: https://shopify.com
1010+
GraphiQL URL: http://localhost:0000/graphiql
1011+
"
1012+
`)
1013+
1014+
// When dev session becomes ready
1015+
vi.mocked(devSessionStatus).mockReturnValue({isDevSessionReady: true})
1016+
1017+
// Wait for the polling interval to update the UI
1018+
await waitForContent(renderInstance, 'preview in your browser')
1019+
1020+
// Then - preview shortcut should be visible
1021+
expect(unstyled(renderInstance.lastFrame()!).replace(/\d/g, '0')).toMatchInlineSnapshot(`
1022+
"
1023+
────────────────────────────────────────────────────────────────────────────────────────────────────
1024+
1025+
› Press g │ open GraphiQL (Admin API) in your browser
1026+
› Press p │ preview in your browser
1027+
› Press q │ quit
1028+
1029+
Preview URL: https://shopify.com
1030+
GraphiQL URL: http://localhost:0000/graphiql
1031+
"
1032+
`)
1033+
1034+
// unmount so that polling is cleared after every test
1035+
renderInstance.unmount()
1036+
})
9711037
})
9721038

9731039
describe('calculatePrefixColumnSize', () => {

packages/app/src/cli/services/dev/ui/components/Dev.tsx

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import metadata from '../../../../metadata.js'
22
import {DeveloperPlatformClient} from '../../../../utilities/developer-platform-client.js'
33
import {ExtensionInstance} from '../../../../models/extensions/extension-instance.js'
4+
import {devSessionStatus} from '../../processes/dev-session.js'
45
import {OutputProcess} from '@shopify/cli-kit/node/output'
56
import {ConcurrentOutput} from '@shopify/cli-kit/node/ui/components'
67
import {useAbortSignal} from '@shopify/cli-kit/node/ui/hooks'
@@ -64,6 +65,7 @@ const Dev: FunctionComponent<DevProps> = ({
6465

6566
const {isRawModeSupported: canUseShortcuts} = useStdin()
6667
const pollingInterval = useRef<NodeJS.Timeout>()
68+
const devSessionPollingInterval = useRef<NodeJS.Timeout>()
6769
const localhostGraphiqlUrl = `http://localhost:${graphiqlPort}/graphiql`
6870
const defaultStatusMessage = `Preview URL: ${previewUrl}${
6971
graphiqlUrl ? `\nGraphiQL URL: ${localhostGraphiqlUrl}` : ''
@@ -83,11 +85,13 @@ const Dev: FunctionComponent<DevProps> = ({
8385
}, 2000)
8486
}
8587
clearInterval(pollingInterval.current)
88+
clearInterval(devSessionPollingInterval.current)
8689
await app.developerPlatformClient.devSessionDelete({appId: app.id, shopFqdn})
8790
await developerPreview.disable()
8891
})
8992

9093
const [devPreviewEnabled, setDevPreviewEnabled] = useState<boolean>(true)
94+
const [devSessionEnabled, setDevSessionEnabled] = useState<boolean>(devSessionStatus().isDevSessionReady)
9195
const [error, setError] = useState<string | undefined>(undefined)
9296

9397
const errorHandledProcesses = useMemo(() => {
@@ -106,6 +110,32 @@ const Dev: FunctionComponent<DevProps> = ({
106110
})
107111
}, [processes, abortController])
108112

113+
/*
114+
* Poll Dev Session status
115+
*
116+
* Polling mechanism to check if the dev session is ready.
117+
* When the session is ready, the polling stops and the shortcuts are shown.
118+
* Reason is that shortcuts won't work properly until the session is ready and the app is installed.
119+
*
120+
* This only applies for App Management dev-sessions.
121+
*/
122+
useEffect(() => {
123+
const pollDevSession = async () => {
124+
const {isDevSessionReady} = devSessionStatus()
125+
setDevSessionEnabled(isDevSessionReady)
126+
if (isDevSessionReady) clearInterval(devSessionPollingInterval.current)
127+
}
128+
129+
if (app.developerPlatformClient.supportsDevSessions) {
130+
// eslint-disable-next-line @typescript-eslint/no-misused-promises
131+
devSessionPollingInterval.current = setInterval(pollDevSession, 200)
132+
} else {
133+
setDevSessionEnabled(true)
134+
}
135+
136+
return () => clearInterval(devSessionPollingInterval.current)
137+
}, [devSessionStatus])
138+
109139
useEffect(() => {
110140
const pollDevPreviewMode = async () => {
111141
try {
@@ -160,12 +190,12 @@ const Dev: FunctionComponent<DevProps> = ({
160190
try {
161191
setError('')
162192

163-
if (input === 'p' && previewUrl) {
193+
if (input === 'p' && previewUrl && devSessionEnabled) {
164194
await metadata.addPublicMetadata(() => ({
165195
cmd_dev_preview_url_opened: true,
166196
}))
167197
await openURL(previewUrl)
168-
} else if (input === 'g' && graphiqlUrl) {
198+
} else if (input === 'g' && graphiqlUrl && devSessionEnabled) {
169199
await metadata.addPublicMetadata(() => ({
170200
cmd_dev_graphiql_opened: true,
171201
}))
@@ -244,15 +274,17 @@ const Dev: FunctionComponent<DevProps> = ({
244274
{devPreviewEnabled ? <Text color="green">✔ on</Text> : <Text color="red">✖ off</Text>}
245275
</Text>
246276
) : null}
247-
{graphiqlUrl ? (
277+
{graphiqlUrl && devSessionEnabled ? (
248278
<Text>
249279
{figures.pointerSmall} Press <Text bold>g</Text> {figures.lineVertical} open GraphiQL (Admin API) in
250280
your browser
251281
</Text>
252282
) : null}
253-
<Text>
254-
{figures.pointerSmall} Press <Text bold>p</Text> {figures.lineVertical} preview in your browser
255-
</Text>
283+
{devSessionEnabled ? (
284+
<Text>
285+
{figures.pointerSmall} Press <Text bold>p</Text> {figures.lineVertical} preview in your browser
286+
</Text>
287+
) : null}
256288
<Text>
257289
{figures.pointerSmall} Press <Text bold>q</Text> {figures.lineVertical} quit
258290
</Text>

0 commit comments

Comments
 (0)