Skip to content
This repository has been archived by the owner on Mar 12, 2020. It is now read-only.

Port Relay test tooling from Reaction, update to Node 10 #1209

Merged
merged 9 commits into from
Nov 1, 2018

Conversation

javamonn
Copy link
Contributor

@javamonn javamonn commented Oct 30, 2018

#skip_new_tests

I ported the existing tests in Reaction over, but not all new files had/need test coverage.

@peril-staging
Copy link
Contributor

peril-staging bot commented Oct 30, 2018

New dependencies added: relay-mock-network-layer.

relay-mock-network-layer

Author: Rob Richard

Description: Relay modern network layer that returns schema correct mock data

Homepage: https://github.com/1stdibs/relay-mock-network-layer#readme

Createdabout 1 year ago
Last Updated7 days ago
LicenseMIT
Maintainers2
Releases6
Direct Dependenciesgraphql-tools
README

relay-mock-network-layer

Provides a network layer for relay modern that returns schema-correct mock data using addMockFunctionsToSchema from graphql-tools;

This is useful for working in an environment like storybook where you want to work on your components without hitting a live GraphQL server.

Usage

import {
	Environment,
	Network,
	RecordSource,
	Store
} from 'relay-runtime';
import getNetworkLayer from 'relay-mock-network-layer';
import schema from './graphql.schema.json';

const network = Network.create(getNetworkLayer({
    schema,
    // pass custom mocks as documented in graphql-tools
    // http://dev.apollodata.com/tools/graphql-tools/mocking.html#Customizing-mocks
    mocks: {
        Geography: () => ({
            id: '2',
            countries: [{
                abbreviation: 'US',
                name: 'United States'
            }, {
                abbreviation: 'UK',
                name: 'United Kingdom'
            }],
                usStates: [{
                abbreviation: 'NY',
                name: 'New York'
            }, {
                abbreviation: 'NJ',
                name: 'New Jersey'
            }]
        }),
        Address: () => ({
            country: 'US',
            city: 'New York',
            state: 'NY',
            zipCode: '10012',
        })
    }
}));

// Create an environment using this network:
const store = new Store(new RecordSource());
const environment = new Environment({network, store});

// use environment in <QueryRenderer>

Mocking custom scalar types

If your schema has custom scalar types you'll need to use the resolvers option to ensure those types get mocked correctly. Pass this option an object containing a resolve function for each custom scalar.

...
import getNetworkLayer from 'relay-mock-network-layer';
import {GraphQLScalarType} from 'graphql';

...

getNetworkLayer({
    schema,
    mocks: {...},
    resolvers: {
        CustomScalar: new GraphQLScalarType({
            name: 'CustomScalar',
            parseLiteral: () => {},
            parseValue: () => {},
            serialize: () => {}
        }),
        CustomScalar2: ...
    }
});

Generated by 🚫 dangerJS

@DangerCI
Copy link

DangerCI commented Oct 30, 2018

Warnings
⚠️

This PR comes from a fork, and won't get JS generated for QA testing this PR inside the Emission Example app.

⚠️

Missing Test Files:

  • src/lib/Components/__tests__/Spinner-tests.tsx
  • src/lib/tests/__tests__/MockRelayRenderer-tests.tsx
  • src/lib/tests/__tests__/RelayStubProvider-tests.ts
  • src/lib/tests/createMockNetworkLayer/CustomScalars/__tests__/formatted_number-tests.ts
  • src/lib/tests/createMockNetworkLayer/__tests__/index-tests.ts
  • src/lib/utils/__tests__/Context-tests.tsx

If these files are supposed to not exist, please update your PR body to include "#skip_new_tests".

Generated by 🚫 dangerJS

package.json Outdated
@@ -30,7 +30,7 @@
"test": "jest",
"test:ci": "jest --outputFile test-results.json --json --runInBand",
"testing": "jest --watch",
"relay": "relay-compiler --src ./src --schema data/schema.graphql --language typescript --artifactDirectory ./src/__generated__ --persist --persistOutput ./src/__generated__/complete.queryMap.json",
"relay": "relay-compiler --src ./src --schema data/schema.graphql --language typescript --artifactDirectory ./src/__generated__ --exclude '**/node_modules/**,**/__mocks__/**,**/__generated__/**'",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

relay-mock-network-layer, used in createMockNetworkLayer, depends on query text being present in the operation in order to client side resolve. Our current approach of used persisted queries however sets text to null in generated .graphql artifacts so that queries are not sent over the network. We obviously don't want to disable persisted queries just to get these tests to work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some ideas @orta had to workaround this:

  • Update compiler to include query text as some other field in the generated artifact, presumably wouldn't be sent over the wire at runtime as the runtime wouldn't know about the generated field.
  • Update relay-mock-network-layer to resolve operation text differently

* tree.
*/
export const RelayFinishedLoading: RenderUntilPredicate<any, any, any> = tree =>
!tree.find(`[testID=${LoadingTestID}]`).length
Copy link
Contributor Author

@javamonn javamonn Oct 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation of renderRelayTree in reaction uses a class name as a loading sentinel. RN does not have classes, and (I think?) we need something that is flushed to a view primitive. I opted for View's testID prop. It comes with a caveat:

This disables the 'layout-only view removal' optimization for this view!

Though I think this is safe to ignore as we only pass it during tests. This gets passed wherever renderWithLoadProgress is used, though I still believe the affected surface area is small - this would only affect Spinner.

"id": "ff999f5e1d50496e9b0dcfc8e6561436",
"text": null,
"id": null,
"text": "query ActiveBidsQuery {\n me {\n ...ActiveBids_me\n __id\n }\n}\n\nfragment ActiveBids_me on Me {\n lot_standings(live: true) {\n most_recent_bid {\n __id\n }\n ...ActiveBid_bid\n }\n __id\n}\n\nfragment ActiveBid_bid on LotStanding {\n is_leading_bidder\n sale {\n href\n is_live_open\n __id\n }\n most_recent_bid {\n __id\n max_bid {\n display\n }\n sale_artwork {\n artwork {\n href\n image {\n url\n }\n artist_names\n __id\n }\n counts {\n bidder_positions\n }\n highest_bid {\n display\n __id: id\n }\n lot_number\n reserve_status\n __id\n }\n }\n}\n",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ^ is the query text needed in the mock layer ^

src/setupJest.ts Outdated
}

global.window = new JSDOM("<!doctype html><html><body></body></html>").window
global.document = window.document
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was copied over from the approach seen here: enzymejs/enzyme#1436 (comment)

This is preferable (imo) to the alternate approach seen further up that thread (enzymejs/enzyme#1436 (comment)) and posted in slack of react-native-mock-render as it requires no component mocking. Still, this approach is non-ideal as RN is not the DOM, but this is enough to get Enzyme to work for the time being. Ideally Enzyme would support RN first-class with an RN adapter, which enzymejs/enzyme#1436 tracks discussion of.

@javamonn javamonn force-pushed the port-relay-devtools branch 3 times, most recently from 58b7ac1 to d4d2df8 Compare October 31, 2018 19:13
"graphql": "^0.13",
"relay-runtime": "https://github.com/alloy/relay/releases/download/v1.5.0-artsy.5/relay-runtime-1.5.0-artsy.5.tgz",
"@types/relay-runtime": "^1.3.5"
"@types/relay-runtime": "^1.3.5",
"cheerio": "0.22.0"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jest-environment-jsdom depends on cheerio@^1.0.0-rc.2. I ran into a serialization error with this version, where <Image /> elements rendered to string would be transformed into <img> instead of the expected <image />:

 renderRelayTree › resolves a promise once the full tree (including nested query renderers) has been rendered

    expect(received).toEqual(expected)

    Expected value to equal:
      "<view><img source=\"[object Object]\"><text ellipsizemode=\"tail\">Mona Lisa</text><text ellipsizemode=\"tail\">Leonardo da Vinci</text></view>"
    Received:
      "<view><image source=\"[object Object]\"></image><text ellipsizemode=\"tail\">Mona Lisa</text><text ellipsizemode=\"tail\">Leonardo da Vinci</text></view>"

My hunch is that enzyme must be passing slightly different configuration options down to cheerio when calling render vs mount, but not sure. Resolving to the latest non-rc cheerio version fixes this, but does appear to be a hack.

@@ -61,10 +61,10 @@
"lib"
],
"resolutions": {
"parse5": "5.1.0",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This resolution does not appear to be necessary anymore. Original discussion here: https://github.com/artsy/emission/pull/1185/files#r217160876

@@ -116,12 +116,13 @@
"enzyme": "^3.1.0",
"enzyme-adapter-react-16": "^1.0.2",
"husky": "^0.14.3",
"jest": "^22.4.2",
"jest": "^23.6.0",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to upgrade jest as part of upgrading to node 10 due to needing this PR: jestjs/jest#6505

- // See https://github.com/facebook/relay/issues/1816
+ const query =
+ operation.text ||
+ (resolveQueryFromOperation && resolveQueryFromOperation(operation));
Copy link
Contributor Author

@javamonn javamonn Oct 31, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lots of diff in this file due to running Prettier against it, but this is the only meaningful change. @orta and I paired on adding this in in response to the issue described in the frontend practice meeting. To put it into text: client side query resolving requires access to the relay query operation text which is not immediately available due to Emission's use of persisted queries, where only the ID is in the compiled artifact. This change allows us to pass in a query resolver function, where we then use the same approach used in fetchQuery.ts to resolve the ID in the query map.

Our usage of this can be seen here: https://github.com/javamonn/emission/blob/084f721f766476e497ecd078add48c5de5050db8/src/lib/tests/createMockNetworkLayer/index.ts#L21-L23

I will PR these changes against the upstream repository once we land them here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heads up that I have a PR over in relay-mock-network-layer that will allow us to pass additional options over: 1stdibs/relay-mock-network-layer#7

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@javamonn @orta Can you elaborate on why you decided against disabling persisted queries in development?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mainly that it increases the difference between dev and prod

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok.

@@ -9,13 +9,7 @@ exports[`renders properly 1`] = `
}
>
<View
accessibilityComponentType={undefined}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to regenerate all snapshots, I'm assuming due to having upgraded Jest.

@javamonn javamonn changed the title WIP: Port Relay test tooling from Reaction Port Relay test tooling from Reaction, update to Node 10 Oct 31, 2018
@javamonn
Copy link
Contributor Author

There's a fair amount of console.error noise in the jest output which is expected due to the jsdom setup, but I'll look into if there's a way we can somehow selectively silence these:

    console.error node_modules/fbjs/lib/warning.js:33
      Warning: React does not recognize the `ellipsizeMode` prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase `ellipsizemode` instead. If you accidentally passed it from a parent component, remove it from the DOM element.
          in Text (created by Text)
          in Text (created by Component)
          in View (created by View)
          in View (created by Component)
          in Component (created by WrapperComponent)
          in WrapperComponent
    console.error node_modules/fbjs/lib/warning.js:33
      Warning: <View /> is using uppercase HTML. Always use lowercase HTML tags in React.
    console.error node_modules/fbjs/lib/warning.js:33
      Warning: The tag <View> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.
    console.error node_modules/fbjs/lib/warning.js:33
      Warning: <Text /> is using uppercase HTML. Always use lowercase HTML tags in React.
    console.error node_modules/fbjs/lib/warning.js:33
      Warning: <View /> is using uppercase HTML. Always use lowercase HTML tags in React.

// Typescript thinks we're in React Native, this file should be test-only i.e. executed within Node.
declare const __dirname: string
// @ts-ignore
import * as fs from "fs"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a better way to make TS happy here? ts-jest takes a tsConfig option that looks promising, but I'm not knowledgeable enough about how the current setup works.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, but that's worth doing in a separate PR IMO

return <Context.Provider value={providerValues}>{children}</Context.Provider>
}

export const ContextConsumer = Context.Consumer
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this context provider be moved out of the tests folder?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, nice call. Stuck it in there as only MockRelayRenderer depends on it, but no reason we can't start using it more widely.

@damassi
Copy link
Member

damassi commented Oct 31, 2018

Very nice PR @javamonn 💯

@orta
Copy link
Contributor

orta commented Nov 1, 2018

Yep, cool - will rebase for you

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants