Skip to content

Commit

Permalink
Log Required fields even if not missing data
Browse files Browse the repository at this point in the history
Reviewed By: josephsavona

Differential Revision: D46612766

fbshipit-source-id: 8c27a1714d2adb973cea64f0bd16f6f95023b909
  • Loading branch information
captbaritone authored and facebook-github-bot committed Jun 12, 2023
1 parent 7a14de0 commit 4a1d71d
Show file tree
Hide file tree
Showing 4 changed files with 340 additions and 3 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
* @oncall relay
*/

'use strict';
import type {MutableRecordSource} from 'relay-runtime/store/RelayStoreTypes';
import type {RequiredFieldLoggerEvent} from 'relay-runtime/store/RelayStoreTypes';

const useFragmentOriginal_REACT_CACHE = require('../react-cache/useFragment_REACT_CACHE');
const RelayEnvironmentProvider = require('../RelayEnvironmentProvider');
const useFragmentOriginal_LEGACY = require('../useFragment');
const useLazyLoadQuery = require('../useLazyLoadQuery');
const React = require('react');
const TestRenderer = require('react-test-renderer');
const {graphql} = require('relay-runtime');
const RelayNetwork = require('relay-runtime/network/RelayNetwork');
const LiveResolverStore = require('relay-runtime/store/experimental-live-resolvers/LiveResolverStore');
const RelayModernEnvironment = require('relay-runtime/store/RelayModernEnvironment');
const RelayRecordSource = require('relay-runtime/store/RelayRecordSource');
const {
disallowConsoleErrors,
disallowWarnings,
} = require('relay-test-utils-internal');

disallowWarnings();
disallowConsoleErrors();

describe.each([
['React Cache', useFragmentOriginal_REACT_CACHE],
['Legacy', useFragmentOriginal_LEGACY],
])('useFragment (%s)', (_hookName, useFragmentOriginal) => {
test('@required(action: LOG) gets logged even if no data is "missing"', () => {
function InnerTestComponent({id}: {id: string}) {
const data = useLazyLoadQuery(
graphql`
query useFragmentWithRequiredTestQuery($id: ID!) {
node(id: $id) {
... on User {
...useFragmentWithRequiredTestUserFragment
}
}
}
`,
{id},
{fetchPolicy: 'store-only'},
);
const user = useFragmentOriginal(
graphql`
fragment useFragmentWithRequiredTestUserFragment on User {
name @required(action: LOG)
}
`,
data.node,
);
return `${user?.name ?? 'Unknown name'}`;
}

function TestComponent({
environment,
...rest
}: {
environment: RelayModernEnvironment,
id: string,
}) {
return (
<RelayEnvironmentProvider environment={environment}>
<React.Suspense fallback="Loading...">
<InnerTestComponent {...rest} />
</React.Suspense>
</RelayEnvironmentProvider>
);
}
const requiredFieldLogger = jest.fn<
$FlowFixMe | [RequiredFieldLoggerEvent],
void,
>();
function createEnvironment(source: MutableRecordSource) {
return new RelayModernEnvironment({
network: RelayNetwork.create(jest.fn()),
store: new LiveResolverStore(source),
requiredFieldLogger,
});
}

const source = RelayRecordSource.create({
'client:root': {
__id: 'client:root',
__typename: '__Root',
'node(id:"1")': {__ref: '1'},
},
'1': {
__id: '1',
__typename: 'User',
name: null,
},
});
const environment = createEnvironment(source);

const renderer = TestRenderer.create(
<TestComponent environment={environment} id="1" />,
);

// Validate that the missing required field was logged.
expect(requiredFieldLogger.mock.calls).toEqual([
[
{
fieldPath: 'name',
kind: 'missing_field.log',
owner: 'useFragmentWithRequiredTestUserFragment',
},
],
]);
expect(renderer.toJSON()).toEqual('Unknown name');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -506,11 +506,12 @@ function useFragmentInternal_REACT_CACHE(
throw pendingOperationsResult.promise;
}
}
// Report required fields only if we're not suspending, since that means
// they're missing even though we are out of options for possibly fetching them:
handlePotentialSnapshotErrorsForState(environment, state);
}

// Report required fields only if we're not suspending, since that means
// they're missing even though we are out of options for possibly fetching them:
handlePotentialSnapshotErrorsForState(environment, state);

useEffect(() => {
// Check for updates since the state was rendered
let currentState = subscribedState;
Expand Down

0 comments on commit 4a1d71d

Please sign in to comment.