diff --git a/.changeset/chilled-buttons-grow.md b/.changeset/chilled-buttons-grow.md
new file mode 100644
index 0000000000..a83675e6ae
--- /dev/null
+++ b/.changeset/chilled-buttons-grow.md
@@ -0,0 +1,5 @@
+---
+'houdini': patch
+---
+
+avoid manipulating scalars with null values
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 4562601f90..e8d96afa04 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -18,17 +18,24 @@ jobs:
runs-on: ubuntu-latest
steps:
+ - name: Checkout source
+ uses: actions/checkout@master
+ with:
+ ref: ${{ github.ref }}
+
- name: Setup Node
- uses: actions/setup-node@v1
+ uses: actions/setup-node@v3
with:
node-version: 16.14.2
+ cache: 'yarn'
- - name: Checkout source
- uses: actions/checkout@master
+ - uses: actions/cache@v2
with:
- ref: ${{ github.ref }}
+ path: '**/node_modules'
+ key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
- name: Install Dependencies
+ if: steps.yarn-cache.outputs.cache-hit != 'true'
run: yarn install
env:
YARN_ENABLE_IMMUTABLE_INSTALLS: false
diff --git a/integration/.eslintrc.cjs b/integration/.eslintrc.cjs
index acc9a701a6..2e05cb6515 100644
--- a/integration/.eslintrc.cjs
+++ b/integration/.eslintrc.cjs
@@ -18,6 +18,7 @@ module.exports = {
node: true
},
rules: {
- '@typescript-eslint/ban-ts-comment': 'off'
+ '@typescript-eslint/ban-ts-comment': 'off',
+ '@typescript-eslint/ban-types': 'off'
}
};
diff --git a/integration/api/graphql.mjs b/integration/api/graphql.mjs
index e1c2a68da1..04f59fa818 100644
--- a/integration/api/graphql.mjs
+++ b/integration/api/graphql.mjs
@@ -60,9 +60,16 @@ export const resolvers = {
},
user: async (_, args) => {
// simulate network delay
- // await sleep(1000);
+ if (args.delay) {
+ await sleep(args.delay);
+ }
const user = getSnapshot(args.snapshot).find((c) => c.id === `${args.snapshot}:${args.id}`);
+
+ if (args.forceNullDate) {
+ user.birthDate = null;
+ }
+
if (!user) {
throw new GraphQLYogaError('User not found', { code: 404 });
}
@@ -75,6 +82,7 @@ export const resolvers = {
const [snapshot, id] = nodeID.split(':');
const list = getSnapshot(snapshot);
const user = list.find((u) => u.id === nodeID);
+
return {
...user,
__typename: 'User'
diff --git a/integration/api/schema.graphql b/integration/api/schema.graphql
index 5a99905e7d..772629e48b 100644
--- a/integration/api/schema.graphql
+++ b/integration/api/schema.graphql
@@ -39,7 +39,7 @@ type PageInfo {
type Query {
avgYearsBirthDate: Float!
node(id: ID!): Node
- user(id: ID!, snapshot: String!, tmp: Boolean): User!
+ user(id: ID!, snapshot: String!, tmp: Boolean, delay: Int, forceNullDate: Boolean): User!
usersConnection(
after: String
before: String
@@ -52,7 +52,7 @@ type Query {
}
type User implements Node {
- birthDate: DateTime!
+ birthDate: DateTime
friendsConnection(after: String, before: String, first: Int, last: Int): UserConnection!
friendsList(limit: Int, offset: Int): [User!]!
id: ID!
diff --git a/integration/houdini.config.js b/integration/houdini.config.js
index 331d0f6e37..3c79cebadc 100644
--- a/integration/houdini.config.js
+++ b/integration/houdini.config.js
@@ -6,6 +6,7 @@ const config = {
module: 'esm',
apiUrl: 'http://localhost:4000/graphql',
defaultCachePolicy: 'CacheOrNetwork',
+ defaultPartial: true,
routes: (filepath) => filepath && !filepath.includes('_') && !filepath.includes('+'),
scalars: {
DateTime: {
diff --git a/integration/src/lib/QueryComponent.svelte b/integration/src/lib/QueryComponent.svelte
index d0ad8490e6..716d4fea3e 100644
--- a/integration/src/lib/QueryComponent.svelte
+++ b/integration/src/lib/QueryComponent.svelte
@@ -9,6 +9,7 @@
+ISO:
{$data?.user.birthDate.toISOString()}
+
+
+
+Local:
+
+ {$data?.user.birthDate.toLocaleString()}
+
diff --git a/integration/src/routes/stores/partial/partial.spec.ts b/integration/src/routes/stores/partial/partial.spec.ts
new file mode 100644
index 0000000000..5445d55fc4
--- /dev/null
+++ b/integration/src/routes/stores/partial/partial.spec.ts
@@ -0,0 +1,42 @@
+import { sleep } from '@kitql/helper';
+import { test } from '@playwright/test';
+import { routes } from '../../../lib/utils/routes.js';
+import { clientSideNavigation, expectToBe } from '../../../lib/utils/testsHelper.js';
+
+test.describe('Partial Pages', () => {
+ test('From the list to the detail should see 2 info then the date coming', async ({ page }) => {
+ // Go to the list
+ await page.goto(routes.Stores_Partial_List);
+
+ // We should have the list
+ expectToBe(
+ page,
+ 'Full Light Partial:1 - Bruce Willis Full Light Partial:2 - Samuel Jackson Full Light Partial:3 - Morgan Freeman Full Light Partial:4 - Tom Hanks'
+ );
+
+ // Go on the light page 2
+ await page.locator('a[id="l_2"]').click();
+
+ // Wait a bit so that the server respond with 2 fields
+ await sleep(2345);
+
+ // Check that we have 2 fields
+ expectToBe(page, 'Partial:2', 'div[id="id"]');
+ expectToBe(page, 'Samuel Jackson', 'div[id="name"]');
+
+ // go back to the list
+ await clientSideNavigation(page, routes.Stores_Partial_List);
+
+ // Go on the light page 2 FULL
+ await page.locator('a[id="f_2"]').click();
+
+ // Click on the link and check directly the 3 divs
+ expectToBe(page, 'Partial:2', 'div[id="id"]');
+ expectToBe(page, 'Samuel Jackson', 'div[id="name"]');
+ expectToBe(page, 'undefined', 'div[id="birthDate"]');
+
+ // Wait a bit so that the server respond and birthDate is displayed
+ await sleep(2345);
+ expectToBe(page, '1948-12-21T00:00:00.000Z', 'div[id="birthDate"]');
+ });
+});
diff --git a/integration/src/routes/stores/partial/partial_List.gql b/integration/src/routes/stores/partial/partial_List.gql
new file mode 100644
index 0000000000..c1be7173f5
--- /dev/null
+++ b/integration/src/routes/stores/partial/partial_List.gql
@@ -0,0 +1,6 @@
+query Partial_List {
+ usersList(snapshot: "Partial") {
+ id
+ name
+ }
+}
diff --git a/integration/src/routes/stores/partial/partial_List.svelte b/integration/src/routes/stores/partial/partial_List.svelte
new file mode 100644
index 0000000000..d59be907b8
--- /dev/null
+++ b/integration/src/routes/stores/partial/partial_List.svelte
@@ -0,0 +1,28 @@
+
+
+
+
+
+ {#each $GQL_Partial_List.data?.usersList ?? [] as { id, name }}
+ {@const realId = id.split(':')[1]}
+
+ {/each}
+
diff --git a/integration/src/routes/stores/partial/partial_[userId].gql b/integration/src/routes/stores/partial/partial_[userId].gql
new file mode 100644
index 0000000000..c87318ce68
--- /dev/null
+++ b/integration/src/routes/stores/partial/partial_[userId].gql
@@ -0,0 +1,7 @@
+query Partial_User($id: ID!) {
+ user(id: $id, snapshot: "Partial", delay: 1000) {
+ id
+ name
+ birthDate
+ }
+}
diff --git a/integration/src/routes/stores/partial/partial_[userId].svelte b/integration/src/routes/stores/partial/partial_[userId].svelte
new file mode 100644
index 0000000000..2516599b61
--- /dev/null
+++ b/integration/src/routes/stores/partial/partial_[userId].svelte
@@ -0,0 +1,38 @@
+
+
+
+
+Back to the list
+
+
+
+
+
+ {$GQL_Partial_User.data?.user.id}
+
+
+ {$GQL_Partial_User.data?.user.name}
+
+
+ {$GQL_Partial_User.data?.user.birthDate?.toISOString()}
+
diff --git a/integration/src/routes/stores/partial/partial_[userId]_Light.gql b/integration/src/routes/stores/partial/partial_[userId]_Light.gql
new file mode 100644
index 0000000000..317037c2ff
--- /dev/null
+++ b/integration/src/routes/stores/partial/partial_[userId]_Light.gql
@@ -0,0 +1,6 @@
+query Partial_User_Light($id: ID!) @cache(partial: true) {
+ user(id: $id, snapshot: "Partial", delay: 1000) {
+ id
+ name
+ }
+}
diff --git a/integration/src/routes/stores/partial/partial_[userId]_Light.svelte b/integration/src/routes/stores/partial/partial_[userId]_Light.svelte
new file mode 100644
index 0000000000..88656e7299
--- /dev/null
+++ b/integration/src/routes/stores/partial/partial_[userId]_Light.svelte
@@ -0,0 +1,35 @@
+
+
+
+
+Back to the list
+
+
+
+
+
+ {$GQL_Partial_User_Light.data?.user.id}
+
+
+ {$GQL_Partial_User_Light.data?.user.name}
+
diff --git a/src/runtime/lib/scalars.test.ts b/src/runtime/lib/scalars.test.ts
index d4581bc3cf..0ac005d524 100644
--- a/src/runtime/lib/scalars.test.ts
+++ b/src/runtime/lib/scalars.test.ts
@@ -1,8 +1,8 @@
+import { jest } from '@jest/globals'
import { testConfigFile } from '../../common'
import { RequestContext } from './network'
import { marshalSelection, unmarshalSelection } from './scalars'
import { ArtifactKind, QueryArtifact } from './types'
-import { jest } from '@jest/globals'
jest.mock('../cache', function () {
return
@@ -515,6 +515,34 @@ describe('unmarshal selection', function () {
})
})
+ test('null inside', function () {
+ const data = {
+ item: {
+ createdAt: null,
+ },
+ }
+
+ const selection = {
+ item: {
+ type: 'TodoItem',
+ keyRaw: 'item',
+
+ fields: {
+ createdAt: {
+ type: 'DateTime',
+ keyRaw: 'createdAt',
+ },
+ },
+ },
+ }
+
+ expect(unmarshalSelection(config, selection, data)).toEqual({
+ item: {
+ createdAt: null,
+ },
+ })
+ })
+
test('nested objects', function () {
// the date to compare against
const date = new Date()
diff --git a/src/runtime/lib/scalars.ts b/src/runtime/lib/scalars.ts
index 7f49938dd4..0c107b53f0 100644
--- a/src/runtime/lib/scalars.ts
+++ b/src/runtime/lib/scalars.ts
@@ -157,7 +157,9 @@ export function unmarshalSelection(
unmarshalSelection(config, fields, value),
]
}
-
+ if (value === null) {
+ return [fieldName, value]
+ }
// is the type something that requires marshaling
if (config.scalars?.[type]?.marshal) {
const unmarshalFn = config.scalars[type].unmarshal