From c862a54ae9fa58b2fda8af22320d2cdcbd30120e Mon Sep 17 00:00:00 2001 From: shanejonas Date: Tue, 3 Nov 2020 12:59:37 -0800 Subject: [PATCH 1/2] fix: fallback example pairings to schema examples --- src/ExamplePairing/ExamplePairing.test.tsx | 31 ++++---- src/ExamplePairing/ExamplePairing.tsx | 32 ++++---- src/ExamplePairings/ExamplePairings.test.tsx | 79 +++++++++++++++++++- src/ExamplePairings/ExamplePairings.tsx | 53 +++++++++++-- 4 files changed, 156 insertions(+), 39 deletions(-) diff --git a/src/ExamplePairing/ExamplePairing.test.tsx b/src/ExamplePairing/ExamplePairing.test.tsx index b2fe5cf..5d6c7f2 100644 --- a/src/ExamplePairing/ExamplePairing.test.tsx +++ b/src/ExamplePairing/ExamplePairing.test.tsx @@ -3,26 +3,18 @@ import ReactDOM from "react-dom"; import ExamplePairing from "./ExamplePairing"; import examples from "@open-rpc/examples"; import refParser from "json-schema-ref-parser"; -import { OpenrpcDocument } from "@open-rpc/meta-schema"; +import { MethodObject, OpenrpcDocument, ExamplePairingObject } from "@open-rpc/meta-schema"; it("renders handles no method", async () => { const div = document.createElement("div"); - ReactDOM.render(, div); + ReactDOM.render(, div); expect(div.innerHTML).toBe(""); ReactDOM.unmountComponentAtNode(div); }); it("renders handles no method examples", async () => { const div = document.createElement("div"); - ReactDOM.render(, div); - expect(div.innerHTML).toBe(""); - ReactDOM.unmountComponentAtNode(div); -}); - -it("renders handles no examplePosition", async () => { - const div = document.createElement("div"); - const simpleMath = await refParser.dereference(examples.simpleMath) as OpenrpcDocument; - ReactDOM.render(, div); + ReactDOM.render(, div); expect(div.innerHTML).toBe(""); ReactDOM.unmountComponentAtNode(div); }); @@ -30,7 +22,12 @@ it("renders handles no examplePosition", async () => { it("renders examples", async () => { const div = document.createElement("div"); const simpleMath = await refParser.dereference(examples.simpleMath) as OpenrpcDocument; - ReactDOM.render(, div); + ReactDOM.render( + + , div); expect(div.innerHTML.includes("2")).toBe(true); expect(div.innerHTML.includes("4")).toBe(true); ReactDOM.unmountComponentAtNode(div); @@ -38,7 +35,7 @@ it("renders examples", async () => { it("renders examples with params by-name", async () => { const div = document.createElement("div"); - ReactDOM.render( { type: "string", }, }, - }} examplePosition={0} />, div); + }; + ReactDOM.render( + + , div); expect(div.innerHTML.includes("foo")).toBe(true); expect(div.innerHTML.includes("bar")).toBe(true); ReactDOM.unmountComponentAtNode(div); diff --git a/src/ExamplePairing/ExamplePairing.tsx b/src/ExamplePairing/ExamplePairing.tsx index 5b9e467..b7ca7b1 100644 --- a/src/ExamplePairing/ExamplePairing.tsx +++ b/src/ExamplePairing/ExamplePairing.tsx @@ -3,14 +3,15 @@ import Grid from "@material-ui/core/Grid"; import { Card, CardContent, Theme, withStyles, WithStyles } from "@material-ui/core"; import ReactJson from "react-json-view"; import ReactMarkdown from "react-markdown"; -import { MethodObject, ExampleObject, ExamplePairingObject } from "@open-rpc/meta-schema"; +import { ExampleObject, ExamplePairingObject } from "@open-rpc/meta-schema"; import _ from "lodash"; export type TParamStructure = "either" | "by-name" | "by-position"; interface IProps extends WithStyles { - examplePosition?: number; - method?: MethodObject; + examplePairing?: ExamplePairingObject; + paramStructure?: TParamStructure; + methodName?: string; reactJsonOptions?: any; } @@ -22,37 +23,32 @@ const styles = (theme: Theme) => ({ class ExamplePairing extends Component { public render() { - const { examplePosition, method, classes } = this.props; - if (_.isUndefined(examplePosition)) { + const { examplePairing, paramStructure, classes, methodName } = this.props; + if (_.isUndefined(examplePairing)) { return null; } - let example; - if (method && method.examples && method.examples[examplePosition]) { - example = method.examples[examplePosition] as ExamplePairingObject; - } - if (!example || _.isEmpty(example)) { + if (_.isUndefined(methodName)) { return null; } - const paramStructure: TParamStructure = method?.paramStructure || "either"; const params = paramStructure === "by-name" - ? (example.params as ExampleObject[]).reduce(((memo, p) => { + ? (examplePairing.params as ExampleObject[]).reduce(((memo, p) => { memo[p.name] = p.value; return memo; }), {} as any) - : (example.params as ExampleObject[]).map(((p) => p.value)); + : (examplePairing.params as ExampleObject[]).map(((p) => p.value)); return ( - + - {example.params && } @@ -61,10 +57,10 @@ class ExamplePairing extends Component { - {example.result && } diff --git a/src/ExamplePairings/ExamplePairings.test.tsx b/src/ExamplePairings/ExamplePairings.test.tsx index 8d1e715..bda9753 100644 --- a/src/ExamplePairings/ExamplePairings.test.tsx +++ b/src/ExamplePairings/ExamplePairings.test.tsx @@ -32,10 +32,85 @@ it("renders examples", async () => { - , div); + } /> + , div); expect(div.innerHTML.includes("simpleMathAdditionTwo")).toBe(true); expect(div.innerHTML.includes("2")).toBe(true); expect(div.innerHTML.includes("4")).toBe(true); ReactDOM.unmountComponentAtNode(div); }); + +it("renders examples with only schema examples", async () => { + const div = document.createElement("div"); + const testDoc: OpenrpcDocument = { + info: { + title: "test", + version: "0.0.0", + }, + methods: [ + { + name: "test-method", + params: [{ + name: "testparam1", + schema: { + examples: ["bob"], + type: "string", + }, + }], + result: { + name: "test-method-result", + schema: { + examples: ["potato"], + type: "string", + }, + }, + }, + ], + openrpc: "1.0.0", + }; + ReactDOM.render( + + , div); + expect(div.innerHTML.includes("potato")).toBe(true); + expect(div.innerHTML.includes("bob")).toBe(true); + ReactDOM.unmountComponentAtNode(div); +}); + +it("renders examples with only schema examples and no method", async () => { + const div = document.createElement("div"); + const testDoc: OpenrpcDocument = { + info: { + title: "test", + version: "0.0.0", + }, + methods: [ + { + name: "test-method", + params: [{ + name: "testparam1", + schema: { + examples: ["bob"], + type: "string", + }, + }], + result: { + name: "test-method-result", + schema: { + examples: ["potato"], + type: "string", + }, + }, + }, + ], + openrpc: "1.0.0", + }; + ReactDOM.render( + + , div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/src/ExamplePairings/ExamplePairings.tsx b/src/ExamplePairings/ExamplePairings.tsx index 7665667..b38abfe 100644 --- a/src/ExamplePairings/ExamplePairings.tsx +++ b/src/ExamplePairings/ExamplePairings.tsx @@ -1,7 +1,7 @@ import React, { Component } from "react"; import ExamplePairing from "../ExamplePairing/ExamplePairing"; import { Typography, List, ListItem, ListItemText, Grid, MenuItem, Menu, withStyles } from "@material-ui/core"; -import { MethodObject, ExamplePairingObject } from "@open-rpc/meta-schema"; +import { MethodObject, ExamplePairingObject, ContentDescriptorObject, ReferenceObject } from "@open-rpc/meta-schema"; interface IProps { method?: MethodObject; @@ -15,6 +15,47 @@ interface IState { currentExample?: ExamplePairingObject; } +const newExample: ExamplePairingObject = { + name: "generated-example", + params: [ + ], + result: { + name: "example-result", + value: null, + }, +}; +const getExamplesFromMethod = (method?: MethodObject): ExamplePairingObject[] => { + if (!method) { return []; } + const examples: ExamplePairingObject[] = []; + + (method.params as ContentDescriptorObject[]).forEach((param, index: number) => { + if (param.schema.examples && param.schema.examples.length > 0) { + param.schema.examples.forEach((ex: any, i: number) => { + if (!examples[i]) { + examples.push({ ...newExample }); + } + examples[i].params.push({ + name: param.name, + value: ex, + }); + }); + } + }); + const methodResult = method.result as ContentDescriptorObject; + if (methodResult && methodResult.schema && methodResult.schema.examples && methodResult.schema.examples.length > 0) { + methodResult.schema.examples.forEach((ex: any, i: number) => { + if (!examples[i]) { + examples.push({ ...newExample }); + } + examples[i].result = { + name: methodResult.name, + value: ex, + }; + }); + } + return examples; +}; + class ExamplePairings extends Component { constructor(props: IProps) { super(props); @@ -43,8 +84,10 @@ class ExamplePairings extends Component { this.setState({ anchorEl: null }); } public render() { - const { examples, method } = this.props; + let { examples } = this.props; + const { method } = this.props; const { anchorEl } = this.state; + examples = examples || getExamplesFromMethod(method); if (!examples || examples.length === 0) { return null; } @@ -84,10 +127,10 @@ class ExamplePairings extends Component { - {this.props.examples && + {examples && } From 8ba92b4c014111117adf267e59b913585e532227 Mon Sep 17 00:00:00 2001 From: shanejonas Date: Tue, 3 Nov 2020 15:47:50 -0800 Subject: [PATCH 2/2] fix: improve test coverage --- src/ExamplePairing/ExamplePairing.test.tsx | 2 +- src/ExamplePairings/ExamplePairings.test.tsx | 33 ++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/ExamplePairing/ExamplePairing.test.tsx b/src/ExamplePairing/ExamplePairing.test.tsx index 5d6c7f2..8f4f2f5 100644 --- a/src/ExamplePairing/ExamplePairing.test.tsx +++ b/src/ExamplePairing/ExamplePairing.test.tsx @@ -7,7 +7,7 @@ import { MethodObject, OpenrpcDocument, ExamplePairingObject } from "@open-rpc/m it("renders handles no method", async () => { const div = document.createElement("div"); - ReactDOM.render(, div); + ReactDOM.render(, div); expect(div.innerHTML).toBe(""); ReactDOM.unmountComponentAtNode(div); }); diff --git a/src/ExamplePairings/ExamplePairings.test.tsx b/src/ExamplePairings/ExamplePairings.test.tsx index bda9753..b69decf 100644 --- a/src/ExamplePairings/ExamplePairings.test.tsx +++ b/src/ExamplePairings/ExamplePairings.test.tsx @@ -79,6 +79,39 @@ it("renders examples with only schema examples", async () => { ReactDOM.unmountComponentAtNode(div); }); +it("renders examples with only schema examples with no params", async () => { + const div = document.createElement("div"); + const testDoc: OpenrpcDocument = { + info: { + title: "test", + version: "0.0.0", + }, + methods: [ + { + name: "test-method", + params: [], + result: { + name: "test-method-result", + schema: { + examples: ["potato"], + type: "string", + }, + }, + }, + ], + openrpc: "1.0.0", + }; + ReactDOM.render( + + , div); + expect(div.innerHTML.includes("potato")).toBe(true); + expect(div.innerHTML.includes("bob")).toBe(true); + ReactDOM.unmountComponentAtNode(div); +}); + it("renders examples with only schema examples and no method", async () => { const div = document.createElement("div"); const testDoc: OpenrpcDocument = {