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

Upgrade apollo-client-devtools to v4 #783

Merged
merged 11 commits into from
Jul 28, 2023
3 changes: 1 addition & 2 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@
"__REPORT_REACT_DEVTOOLS_PORT__",
"__FETCH_SUPPORT__",
"__NETWORK_INSPECT__",
"__APOLLO_CLIENT__",
"__APOLLO_DEVTOOLS_SHOULD_DISPLAY_PANEL__"
"__FROM_DEBUGGER_WORKER__"
]
}
],
Expand Down
24 changes: 24 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,20 @@ jobs:
- name: Setup
run: sudo apt-get install -y libgbm-dev
- name: Test
id: test
run: |
yarn
cd npm-package && yarn && cd ..
yarn test
yarn build
xvfb-run --auto-servernum yarn test-e2e
- name: Upload artifacts on failure
if: ${{ failure() && steps.test.conclusion == 'failure' }}
uses: actions/upload-artifact@v3
with:
name: artifacts
path: artifacts
retention-days: 1
build-test-macos:
runs-on: macOS-latest
steps:
Expand All @@ -33,12 +41,20 @@ jobs:
node-version: 18.x
cache: 'yarn'
- name: Test
id: test
run: |
yarn
cd npm-package && yarn && cd ..
yarn test
yarn build
yarn test-e2e
- name: Upload artifacts on failure
if: ${{ failure() && steps.test.conclusion == 'failure' }}
uses: actions/upload-artifact@v3
with:
name: artifacts
path: artifacts
retention-days: 1
build-test-windows:
runs-on: windows-2022
steps:
Expand All @@ -48,6 +64,7 @@ jobs:
node-version: 18.x
cache: 'yarn'
- name: Test
id: test
shell: bash
run: |
yarn config set network-timeout 500000 -g
Expand All @@ -56,3 +73,10 @@ jobs:
yarn build
yarn test
yarn test-e2e
- name: Upload artifacts on failure
if: ${{ failure() && steps.test.conclusion == 'failure' }}
uses: actions/upload-artifact@v3
with:
name: artifacts
path: artifacts
retention-days: 1
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ release/
tmp/
config_test
.idea/
artifacts/
74 changes: 51 additions & 23 deletions __e2e__/app.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import waitForExpect from 'wait-for-expect'
import buildTestBundle, { bundlePath } from './buildTestBundle'
import createMockRNServer from './mockRNServer'

const delay = (time) => new Promise((resolve) => {
setTimeout(resolve, time)
})
const delay = (time) =>
new Promise((resolve) => {
setTimeout(resolve, time)
})

jest.setTimeout(6e4)

Expand All @@ -21,7 +22,6 @@ describe('Application launch', () => {

beforeAll(async () => {
await buildTestBundle()
process.env.E2E_TEST = '1'
process.env.PACKAGE = 'no'
electronApp = await electron.launch({
executablePath,
Expand All @@ -33,6 +33,24 @@ describe('Application launch', () => {
await mainWindow.waitForLoadState()
})

afterEach(async () => {
const state = expect.getState()
await mainWindow.screenshot({
path: `./artifacts/${state.currentTestName}.png`,
})
const devtoolsWindow = electronApp.windows()[2]
if (devtoolsWindow) {
try {
await delay(100)
await devtoolsWindow.screenshot({
path: `./artifacts/devtools:${state.currentTestName}.png`,
})
} catch (e) {
console.error(e)
}
}
})

afterAll(async () => {
await electronApp.close()
})
Expand Down Expand Up @@ -79,11 +97,12 @@ describe('Application launch', () => {
})

const customRNServerPort = 8098
const getURLFromConnection = (server) => new Promise((resolve) => {
server.on('connection', (socket, req) => {
resolve(req.url)
const getURLFromConnection = (server) =>
new Promise((resolve) => {
server.on('connection', (socket, req) => {
resolve(req.url)
})
})
})

it('should connect to fake RN server', async () => {
const { wss, server } = createMockRNServer()
Expand Down Expand Up @@ -139,15 +158,16 @@ describe('Application launch', () => {
})

describe('Import fake script after', () => {
const getOneRequestHeaders = (port) => new Promise((resolve) => {
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' })
res.end('')
resolve(req.headers)
server.close()
const getOneRequestHeaders = (port) =>
new Promise((resolve) => {
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' })
res.end('')
resolve(req.headers)
server.close()
})
server.listen(port)
})
server.listen(port)
})

let headersPromise
let server
Expand Down Expand Up @@ -263,10 +283,11 @@ describe('Application launch', () => {
},
}

const eachAsync = (entries, fn) => entries.reduce(
(promise, entry, index) => promise.then(() => fn(entry, index)),
Promise.resolve(),
)
const eachAsync = (entries, fn) =>
entries.reduce(
(promise, entry, index) => promise.then(() => fn(entry, index)),
Promise.resolve(),
)

const runExpectActions = async (name) => {
const { expt, notExpt } = expectActions[name]
Expand Down Expand Up @@ -310,7 +331,9 @@ describe('Application launch', () => {

it('should have only specific logs in console of main window', async () => {
// Print renderer process logs
logs.forEach((log) => console.log(`Message: ${log.text()}\nType: ${log.type()}`))
logs.forEach((log) =>
console.log(`Message: ${log.text()}\nType: ${log.type()}`),
)
expect(logs.length).toEqual(3) // clear + clear + warning
const [, , formDataWarning] = logs
expect(formDataWarning.type()).toBe('warning')
Expand All @@ -322,9 +345,14 @@ describe('Application launch', () => {
})

it('should show apollo devtools panel', async () => {
const devtoolsWindow = electronApp.windows()[2]
expect(
await mainWindow.evaluate(
() => window.__APOLLO_DEVTOOLS_SHOULD_DISPLAY_PANEL__,
await devtoolsWindow.evaluate(() =>
// eslint-disable-next-line no-undef
Object.keys(UI.panels).some(
(key) =>
key.startsWith('chrome-extension://') && key.endsWith('Apollo'),
),
),
).toBeTruthy()
})
Expand Down
2 changes: 1 addition & 1 deletion __e2e__/buildTestBundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default function buildTestBundle() {
filename,
},
resolve: {
mainFields: ['main', 'browser'],
mainFields: ['react-native', 'main', 'browser'],
},
}).run(resolve)
})
Expand Down
26 changes: 21 additions & 5 deletions __e2e__/fixture/apollo.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
/* eslint-disable import/no-extraneous-dependencies */
/*
* Create an Apollo Client to test the bridge messages sent
* wouldn't break the debugger proxy.
*/

/* eslint-disable */
import ApolloClient from 'apollo-boost';
import { ApolloClient, InMemoryCache } from '@apollo/client'
import gql from 'graphql-tag'

new ApolloClient({
uri: 'https://fakerql.com/graphql',
});
const client = new ApolloClient({
uri: 'https://spacex-production.up.railway.app/',
cache: new InMemoryCache(),
})

export default async function run() {
return client.query({
query: gql`
query ExampleQuery {
company {
name
ceo
employees
}
}
`,
})
}
8 changes: 5 additions & 3 deletions __e2e__/fixture/app.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import './setup'

import runXHRTest from './xhr-test' // Install fetch polyfill before initial apollo-client
import runApolloTest from './apollo'
import runReduxTest from './redux'
import runMobXTest from './mobx'
import runRemoteDevTest from './remotedev'
import runXHRTest from './xhr-test'
import './apollo'

runReduxTest()
runMobXTest()
runRemoteDevTest()
runXHRTest()
runApolloTest().catch((e) => console.error(e))
runXHRTest().catch((e) => console.error(e))
4 changes: 2 additions & 2 deletions __e2e__/fixture/xhr-test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import 'whatwg-fetch'

export default function run() {
export default async function run() {
// Fetch with forbidden header names
fetch('http://localhost:8099', {
await fetch('http://localhost:8099', {
headers: {
Origin: 'custom_origin_here',
'User-Agent': 'react-native',
Expand Down
30 changes: 11 additions & 19 deletions app/middlewares/debuggerAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,14 @@ let host
let port
let socket

const APOLLO_BACKEND = 'apollo-devtools-backend'
const APOLLO_PROXY = 'apollo-devtools-proxy'
const APOLLO_MESSAGE_PREFIX = 'ac-devtools:'

const workerOnMessage = (message) => {
const { data } = message

if (data && data.source === APOLLO_BACKEND) {
if (!window.__APOLLO_DEVTOOLS_SHOULD_DISPLAY_PANEL__) {
window.__APOLLO_DEVTOOLS_SHOULD_DISPLAY_PANEL__ = true
}

postMessage(
{
source: APOLLO_BACKEND,
payload: data,
},
'*',
)
if (data && data.message?.startsWith(APOLLO_MESSAGE_PREFIX)) {
data.__FROM_DEBUGGER_WORKER__ = true
postMessage(data, '*')
return false
}

Expand All @@ -63,13 +53,15 @@ const workerOnMessage = (message) => {

const onWindowMessage = (e) => {
const { data } = e
if (data && data.source === APOLLO_PROXY) {
const message = typeof data.payload === 'string' ? { event: data.payload } : data.payload
if (
!data?.__FROM_DEBUGGER_WORKER__ &&
data?.message?.startsWith(APOLLO_MESSAGE_PREFIX)
) {
worker.postMessage({
method: 'emitApolloMessage',
source: APOLLO_PROXY,
...message,
...data,
})
return false
}
}

Expand All @@ -89,7 +81,7 @@ const shutdownJSRuntime = () => {
scriptExecuted = false
if (worker) {
worker.terminate()
window.removeEventListener('messsage', onWindowMessage)
window.removeEventListener('message', onWindowMessage)
setDevMenuMethods([])
}
worker = null
Expand Down
49 changes: 3 additions & 46 deletions app/worker/apollo.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,5 @@
import Bridge from 'apollo-client-devtools/src/bridge'
import { initBackend, sendBridgeReady } from 'apollo-client-devtools/src/backend'
import { version as devToolsVersion } from 'apollo-client-devtools/package.json'
import { getSafeAsyncStorage } from './asyncStorage'

export function handleApolloClient({ NativeModules } = {}) {
const interval = setInterval(() => {
if (!self.__APOLLO_CLIENT__) {
return
}

clearInterval(interval)

const hook = {
ApolloClient: self.__APOLLO_CLIENT__,
devToolsVersion,
}

let listener

const bridge = new Bridge({
listen(fn) {
listener = self.addEventListener('message', (evt) => {
if (evt.data.source === 'apollo-devtools-proxy') {
return fn(evt.data)
}
})
},
send(data) {
postMessage({
...data,
source: 'apollo-devtools-backend',
})
},
})

bridge.on('init', () => {
sendBridgeReady()
})

bridge.on('shutdown', () => {
self.removeEventListener('message', listener)
})

initBackend(bridge, hook, getSafeAsyncStorage(NativeModules))
}, 1000)
return interval
export function handleApolloClient() {
// eslint-disable-next-line global-require
require('apollo-client-devtools/build/hook')
}
Loading
Loading