Skip to content

Commit

Permalink
Implement catch result type (#549)
Browse files Browse the repository at this point in the history
* implement catch result type

* changelog

* point to correct compiler

* stable tests
  • Loading branch information
zth authored Jan 4, 2025
1 parent a8a95de commit f249f97
Show file tree
Hide file tree
Showing 12 changed files with 1,522 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# master

- Add support for the new Relay `@catch` directive. https://github.com/zth/rescript-relay/pull/549

# 3.1.0

This brings the Relay version to `18.2.0`.
Expand Down
184 changes: 184 additions & 0 deletions packages/rescript-relay/__tests__/Test_catch-tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
require("@testing-library/jest-dom/extend-expect");
const t = require("@testing-library/react");
const React = require("react");
const queryMock = require("./queryMock");

const { test_catch } = require("./Test_catch.bs");

const date = new Date("2025-01-01T06:00");

describe("Catch", () => {
test("logged in user prop - success", async () => {
queryMock.mockQuery({
name: "TestCatchLoggedInUserPropQuery",
data: {
loggedInUser: {
id: "user-1",
createdAt: date.toISOString(),
},
},
});

t.render(test_catch("TestLoggedInUserProp"));
await t.screen.findByText("Got createdAt: 2025-01-01");
});

test("logged in user prop - fail", async () => {
queryMock.mockQuery({
name: "TestCatchLoggedInUserPropQuery",
data: {
loggedInUser: {
id: "user-1",
createdAt: null,
},
},
graphqlErrors: [{ path: ["loggedInUser", "createdAt"] }],
});

t.render(test_catch("TestLoggedInUserProp"));
await t.screen.findByText("Error!");
});

test("logged in user prop from fragment - success", async () => {
queryMock.mockQuery({
name: "TestCatchLoggedInUserPropQuery",
data: {
loggedInUser: {
id: "user-1",
createdAt: date.toISOString(),
},
},
});

t.render(test_catch("TestLoggedInUserPropFragmentData"));
await t.screen.findByText("Got createdAt: 2025-01-01");
});

test("logged in user prop from fragment - fail", async () => {
queryMock.mockQuery({
name: "TestCatchLoggedInUserPropQuery",
data: {
loggedInUser: {
id: "user-1",
createdAt: null,
},
},
graphqlErrors: [{ path: ["loggedInUser", "createdAt"] }],
});

t.render(test_catch("TestLoggedInUserPropFragmentData"));
await t.screen.findByText("Error!");
});

test("member prop - success", async () => {
queryMock.mockQuery({
name: "TestCatchMemberPropQuery",
data: {
member: {
__typename: "User",
id: "user-1",
createdAt: date.toISOString(),
},
},
});

t.render(test_catch("TestMember"));
await t.screen.findByText("Got user id: user-1, and createdAt: 2025-01-01");
});

test("member prop - fail", async () => {
queryMock.mockQuery({
name: "TestCatchMemberPropQuery",
data: {
member: null,
},
graphqlErrors: [{ path: ["member"] }],
});

t.render(test_catch("TestMember"));
await t.screen.findByText("Error!");
});

test("member prop - success nested", async () => {
queryMock.mockQuery({
name: "TestCatchMemberPropNestedQuery",
data: {
member: {
__typename: "User",
id: "user-1",
memberOfSingular: {
__typename: "User",
id: "user-2",
createdAt: date.toISOString(),
},
},
},
});

t.render(test_catch("TestMemberNested"));
await t.screen.findByText("Got user id: user-1, and createdAt: 2025-01-01");
});

test("member prop - fail nested", async () => {
queryMock.mockQuery({
name: "TestCatchMemberPropNestedQuery",
data: {
member: {
__typename: "User",
id: "user-1",
memberOfSingular: {
__typename: "User",
id: "user-2",
createdAt: null,
},
},
},
graphqlErrors: [{ path: ["member", "memberOfSingular", "createdAt"] }],
});

t.render(test_catch("TestMemberNested"));
await t.screen.findByText("Error nested!");
});

test("members array", async () => {
queryMock.mockQuery({
name: "TestCatchMembersPropQuery",
data: {
members: {
edges: [
{
__typename: "UserEdge",
node: {
__typename: "User",
id: "user-1",
createdAt: date.toISOString(),
},
},
{
__typename: "UserEdge",
node: {
__typename: "User",
id: "user-2",
createdAt: null,
},
},
{
__typename: "UserEdge",
node: {
__typename: "User",
id: "user-3",
createdAt: date.toISOString(),
},
},
],
},
},
graphqlErrors: [{ path: ["members", "edges", 1, "node", "createdAt"] }],
});

t.render(test_catch("TestMembers"));
await t.screen.findByText(
"User: user-1 - 2025-01-01, Error!, User: user-3 - 2025-01-01"
);
});
});
178 changes: 178 additions & 0 deletions packages/rescript-relay/__tests__/Test_catch.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
module QueryLoggedInUserProp = %relay(`
query TestCatchLoggedInUserPropQuery {
loggedInUser {
createdAt @catch
...TestCatchUser_user
}
}
`)

module LoggedInUserFragment = %relay(`
fragment TestCatchUser_user on User @catch {
createdAt
}
`)

module TestLoggedInUserProp = {
@react.component
let make = () => {
let query = QueryLoggedInUserProp.use(~variables=())

switch query.loggedInUser.createdAt {
| Ok({value: createdAt}) =>
<div>
{React.string(
"Got createdAt: " ++ createdAt->Js.Date.toISOString->Js.String2.slice(~from=0, ~to_=10),
)}
</div>
| Error(_) => <div> {React.string("Error!")} </div>
}
}
}

module TestLoggedInUserPropFragmentData = {
@react.component
let make = () => {
let query = QueryLoggedInUserProp.use(~variables=())
let fragmentData = LoggedInUserFragment.use(query.loggedInUser.fragmentRefs)

switch fragmentData {
| Ok({value: {createdAt}}) =>
<div>
{React.string(
"Got createdAt: " ++ createdAt->Js.Date.toISOString->Js.String2.slice(~from=0, ~to_=10),
)}
</div>
| Error(_) => <div> {React.string("Error!")} </div>
}
}
}

module QueryMember = %relay(`
query TestCatchMemberPropQuery {
member(id: "123") @catch {
... on User {
id
createdAt
}
}
}
`)

module TestMember = {
@react.component
let make = () => {
let query = QueryMember.use(~variables=())

switch query.member {
| Ok({value: User({id, createdAt})}) =>
<div>
{React.string(
"Got user id: " ++
id ++
", and createdAt: " ++
createdAt->Js.Date.toISOString->Js.String2.slice(~from=0, ~to_=10),
)}
</div>
| Error(_) => <div> {React.string("Error!")} </div>
| _ => React.null
}
}
}

module QueryMemberNested = %relay(`
query TestCatchMemberPropNestedQuery {
member(id: "123") {
... on User {
id
memberOfSingular @catch {
... on User {
id
createdAt
}
}
}
}
}
`)

module TestMemberNested = {
@react.component
let make = () => {
let query = QueryMemberNested.use(~variables=())

switch query.member {
| Some(User({id, memberOfSingular: Ok({value: User({createdAt})})})) =>
<div>
{React.string(
"Got user id: " ++
id ++
", and createdAt: " ++
createdAt->Js.Date.toISOString->Js.String2.slice(~from=0, ~to_=10),
)}
</div>
| Some(User({memberOfSingular: Error(_)})) => <div> {React.string("Error nested!")} </div>
| _ => React.null
}
}
}

module QueryMembers = %relay(`
query TestCatchMembersPropQuery {
members(groupId: "123") {
edges {
node @catch {
... on User {
id
createdAt
}
}
}
}
}
`)

module TestMembers = {
@react.component
let make = () => {
let query = QueryMembers.use(~variables=())

let members =
query.members
->Belt.Option.flatMap(v => v.edges)
->Belt.Option.getWithDefault([])
->Belt.Array.keepMap(x => x->Belt.Option.map(r => r.node))

members
->Js.Array2.map(r =>
switch r {
| Ok({value: User({id, createdAt})}) =>
`User: ${id} - ${createdAt->Js.Date.toISOString->Js.String2.slice(~from=0, ~to_=10)}`
| _ => "Error!"
}
)
->Js.Array2.joinWith(", ")
->React.string
}
}

@live
let test_catch = testName => {
let network = RescriptRelay.Network.makePromiseBased(~fetchFunction=RelayEnv.fetchQuery)

let environment = RescriptRelay.Environment.make(
~network,
~store=RescriptRelay.Store.make(~source=RescriptRelay.RecordSource.make()),
)

<TestProviders.Wrapper environment>
{switch testName {
| "TestLoggedInUserProp" => <TestLoggedInUserProp />
| "TestLoggedInUserPropFragmentData" => <TestLoggedInUserPropFragmentData />
| "TestMember" => <TestMember />
| "TestMemberNested" => <TestMemberNested />
| "TestMembers" => <TestMembers />
| _ => React.null
}}
</TestProviders.Wrapper>
}
2 changes: 2 additions & 0 deletions packages/rescript-relay/__tests__/TestsUtils.res
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ exception Malformed_date
exception Malformed_number

module Datetime = {
@editor.completeFrom(Js.Date)
type t = Js.Date.t

let parse = t =>
switch t->Js.Json.decodeString {
| None => raise(Malformed_date)
Expand Down
Loading

0 comments on commit f249f97

Please sign in to comment.