Begin work on contributions and adapters (#93)
Summary: This commit begins to extend the artifact editor to display contributions. To display contributions from arbitrary plugins, we need to communicate with those plugins somehow. We do so via an adapter interface that plugins implement; included in this commit is an implementation of this interface for the GitHub plugin (partially: we punt on rendering). This includes a snapshot test. The snapshot format is designed to be human-readable and -auditable so that it can serve as documentation. Test Plan: Run the application with `yarn start`. Then, fetch a graph and watch as its contributions appear in the view. wchargin-branch: contributions-and-adapters
This commit is contained in:
parent
e9ca833448
commit
ab619432e1
|
@ -43,6 +43,7 @@
|
|||
"react": "^16.2.0",
|
||||
"react-dev-utils": "^5.0.0",
|
||||
"react-dom": "^16.2.0",
|
||||
"react-test-renderer": "^16.2.0",
|
||||
"style-loader": "0.19.0",
|
||||
"sw-precache-webpack-plugin": "0.11.4",
|
||||
"url-loader": "0.6.2",
|
||||
|
|
|
@ -3,14 +3,23 @@
|
|||
import React from "react";
|
||||
import {StyleSheet, css} from "aphrodite/no-important";
|
||||
|
||||
import type {Node} from "../../../core/graph";
|
||||
import {ArtifactList} from "./ArtifactList";
|
||||
import {GitHubGraphFetcher} from "./GitHubGraphFetcher";
|
||||
import "./pluginAdapter";
|
||||
|
||||
import type {Graph, Node} from "../../../core/graph";
|
||||
import type {ArtifactNodePayload} from "../artifactPlugin";
|
||||
import type {
|
||||
NodePayload as GitHubNodePayload,
|
||||
EdgePayload as GitHubEdgePayload,
|
||||
} from "../../github/githubPlugin";
|
||||
import {ArtifactList} from "./ArtifactList";
|
||||
import {ContributionList} from "./ContributionList";
|
||||
import {GitHubGraphFetcher} from "./GitHubGraphFetcher";
|
||||
import standardAdapterSet from "./standardAdapterSet";
|
||||
|
||||
type Props = {};
|
||||
type State = {
|
||||
artifacts: Node<ArtifactNodePayload>[],
|
||||
githubGraph: ?Graph<GitHubNodePayload, GitHubEdgePayload>,
|
||||
};
|
||||
|
||||
function createSampleArtifact(name) {
|
||||
|
@ -30,6 +39,7 @@ export default class App extends React.Component<Props, State> {
|
|||
super();
|
||||
this.state = {
|
||||
artifacts: [],
|
||||
githubGraph: null,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -39,6 +49,11 @@ export default class App extends React.Component<Props, State> {
|
|||
<header className={css(styles.header)}>
|
||||
<h1>Artifact editor</h1>
|
||||
</header>
|
||||
<GitHubGraphFetcher
|
||||
onCreateGraph={(githubGraph) => {
|
||||
this.setState({githubGraph});
|
||||
}}
|
||||
/>
|
||||
<ArtifactList
|
||||
artifacts={this.state.artifacts}
|
||||
createArtifact={(name) => {
|
||||
|
@ -47,7 +62,10 @@ export default class App extends React.Component<Props, State> {
|
|||
}));
|
||||
}}
|
||||
/>
|
||||
<GitHubGraphFetcher onCreateGraph={(x) => console.log(x)} />
|
||||
<ContributionList
|
||||
graph={this.state.githubGraph}
|
||||
adapters={standardAdapterSet}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
// @flow
|
||||
|
||||
import React from "react";
|
||||
|
||||
import type {Address} from "../../../core/address";
|
||||
import {AdapterSet} from "./adapterSet";
|
||||
import {Graph} from "../../../core/graph";
|
||||
|
||||
type Props = {
|
||||
graph: ?Graph<any, any>,
|
||||
adapters: AdapterSet,
|
||||
};
|
||||
type State = {};
|
||||
|
||||
export class ContributionList extends React.Component<Props, State> {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h2>Contributions</h2>
|
||||
{this.renderTable()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderTable() {
|
||||
if (this.props.graph == null) {
|
||||
return <div>(no graph)</div>;
|
||||
} else {
|
||||
const graph: Graph<any, any> = this.props.graph;
|
||||
return (
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Artifact</th>
|
||||
<th>Weight</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{this.props.graph.getAllNodes().map((node) => {
|
||||
const adapter = this.props.adapters.getAdapter(node);
|
||||
if (adapter == null) {
|
||||
return (
|
||||
<tr>
|
||||
<td colspan={3}>
|
||||
<i>unknown</i> (plugin: {node.address.pluginName})
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<tr>
|
||||
<td>{adapter.extractTitle(graph, node)}</td>
|
||||
<td>[TODO]</td>
|
||||
<td>[TODO]</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
// @flow
|
||||
|
||||
import type {Node} from "../../../core/graph";
|
||||
import type {PluginAdapter} from "./pluginAdapter";
|
||||
|
||||
export class AdapterSet {
|
||||
adapters: {[pluginName: string]: PluginAdapter<any>};
|
||||
|
||||
constructor() {
|
||||
this.adapters = {};
|
||||
}
|
||||
|
||||
addAdapter(adapter: PluginAdapter<any>): void {
|
||||
this.adapters[adapter.pluginName] = adapter;
|
||||
}
|
||||
|
||||
getAdapter<NP>(node: Node<NP>): ?PluginAdapter<NP> {
|
||||
return this.adapters[node.address.pluginName];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,324 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`githubPluginAdapter operates on the example repo 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"id": Object {
|
||||
"id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3MTAwMzE0MDM4",
|
||||
"type": "PULL_REQUEST_REVIEW",
|
||||
},
|
||||
"payload": Object {
|
||||
"body": "I'm sold",
|
||||
"state": "APPROVED",
|
||||
},
|
||||
"rendered": <div>
|
||||
type:
|
||||
PULL_REQUEST_REVIEW
|
||||
(details to be implemented)
|
||||
</div>,
|
||||
"title": "review of #5: This pull request will be more contentious. I can feel it...",
|
||||
"type": "PULL_REQUEST_REVIEW",
|
||||
},
|
||||
Object {
|
||||
"id": Object {
|
||||
"id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3MTAwMzEzODk5",
|
||||
"type": "PULL_REQUEST_REVIEW",
|
||||
},
|
||||
"payload": Object {
|
||||
"body": "hmmm.jpg",
|
||||
"state": "CHANGES_REQUESTED",
|
||||
},
|
||||
"rendered": <div>
|
||||
type:
|
||||
PULL_REQUEST_REVIEW
|
||||
(details to be implemented)
|
||||
</div>,
|
||||
"title": "review of #5: This pull request will be more contentious. I can feel it...",
|
||||
"type": "PULL_REQUEST_REVIEW",
|
||||
},
|
||||
Object {
|
||||
"id": Object {
|
||||
"id": "MDExOlB1bGxSZXF1ZXN0MTcxODg3NzQx",
|
||||
"type": "PULL_REQUEST",
|
||||
},
|
||||
"payload": Object {
|
||||
"body": "Oh look, it's a pull request.",
|
||||
"number": 3,
|
||||
"title": "Add README, merge via PR.",
|
||||
},
|
||||
"rendered": <div>
|
||||
type:
|
||||
PULL_REQUEST
|
||||
(details to be implemented)
|
||||
</div>,
|
||||
"title": "#3: Add README, merge via PR.",
|
||||
"type": "PULL_REQUEST",
|
||||
},
|
||||
Object {
|
||||
"id": Object {
|
||||
"id": "MDExOlB1bGxSZXF1ZXN0MTcxODg4NTIy",
|
||||
"type": "PULL_REQUEST",
|
||||
},
|
||||
"payload": Object {
|
||||
"body": "@wchargin could you please do the following:
|
||||
- add a commit comment
|
||||
- add a review comment requesting some trivial change
|
||||
- i'll change it
|
||||
- then approve the pr",
|
||||
"number": 5,
|
||||
"title": "This pull request will be more contentious. I can feel it...",
|
||||
},
|
||||
"rendered": <div>
|
||||
type:
|
||||
PULL_REQUEST
|
||||
(details to be implemented)
|
||||
</div>,
|
||||
"title": "#5: This pull request will be more contentious. I can feel it...",
|
||||
"type": "PULL_REQUEST",
|
||||
},
|
||||
Object {
|
||||
"id": Object {
|
||||
"id": "MDEyOklzc3VlQ29tbWVudDM2OTE2MjIyMg==",
|
||||
"type": "COMMENT",
|
||||
},
|
||||
"payload": Object {
|
||||
"body": "It seems apropos to reference something from a pull request comment... eg: #2 ",
|
||||
"url": "https://github.com/sourcecred/example-repo/pull/3#issuecomment-369162222",
|
||||
},
|
||||
"rendered": <div>
|
||||
type:
|
||||
COMMENT
|
||||
(details to be implemented)
|
||||
</div>,
|
||||
"title": "comment on #3: Add README, merge via PR.",
|
||||
"type": "COMMENT",
|
||||
},
|
||||
Object {
|
||||
"id": Object {
|
||||
"id": "MDEyOklzc3VlQ29tbWVudDM3Mzc2ODQ0Mg==",
|
||||
"type": "COMMENT",
|
||||
},
|
||||
"payload": Object {
|
||||
"body": "A wild COMMENT appeared!",
|
||||
"url": "https://github.com/sourcecred/example-repo/issues/6#issuecomment-373768442",
|
||||
},
|
||||
"rendered": <div>
|
||||
type:
|
||||
COMMENT
|
||||
(details to be implemented)
|
||||
</div>,
|
||||
"title": "comment on #6: An issue with comments",
|
||||
"type": "COMMENT",
|
||||
},
|
||||
Object {
|
||||
"id": Object {
|
||||
"id": "MDEyOklzc3VlQ29tbWVudDM3Mzc2ODUzOA==",
|
||||
"type": "COMMENT",
|
||||
},
|
||||
"payload": Object {
|
||||
"body": "And the maintainer said, \\"Let there be comments!\\"",
|
||||
"url": "https://github.com/sourcecred/example-repo/issues/6#issuecomment-373768538",
|
||||
},
|
||||
"rendered": <div>
|
||||
type:
|
||||
COMMENT
|
||||
(details to be implemented)
|
||||
</div>,
|
||||
"title": "comment on #6: An issue with comments",
|
||||
"type": "COMMENT",
|
||||
},
|
||||
Object {
|
||||
"id": Object {
|
||||
"id": "MDEyOklzc3VlQ29tbWVudDM3Mzc2ODcwMw==",
|
||||
"type": "COMMENT",
|
||||
},
|
||||
"payload": Object {
|
||||
"body": "It should also be possible to reference by exact url: https://github.com/sourcecred/example-repo/issues/6",
|
||||
"url": "https://github.com/sourcecred/example-repo/issues/2#issuecomment-373768703",
|
||||
},
|
||||
"rendered": <div>
|
||||
type:
|
||||
COMMENT
|
||||
(details to be implemented)
|
||||
</div>,
|
||||
"title": "comment on #2: A referencing issue.",
|
||||
"type": "COMMENT",
|
||||
},
|
||||
Object {
|
||||
"id": Object {
|
||||
"id": "MDEyOklzc3VlQ29tbWVudDM3Mzc2ODg1MA==",
|
||||
"type": "COMMENT",
|
||||
},
|
||||
"payload": Object {
|
||||
"body": "We might also reference individual comments directly.
|
||||
https://github.com/sourcecred/example-repo/issues/6#issuecomment-373768538",
|
||||
"url": "https://github.com/sourcecred/example-repo/issues/2#issuecomment-373768850",
|
||||
},
|
||||
"rendered": <div>
|
||||
type:
|
||||
COMMENT
|
||||
(details to be implemented)
|
||||
</div>,
|
||||
"title": "comment on #2: A referencing issue.",
|
||||
"type": "COMMENT",
|
||||
},
|
||||
Object {
|
||||
"id": Object {
|
||||
"id": "MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDE3MTQ2MDE5OA==",
|
||||
"type": "PULL_REQUEST_REVIEW_COMMENT",
|
||||
},
|
||||
"payload": Object {
|
||||
"body": "seems a bit capricious",
|
||||
"url": "https://github.com/sourcecred/example-repo/pull/5#discussion_r171460198",
|
||||
},
|
||||
"rendered": <div>
|
||||
type:
|
||||
PULL_REQUEST_REVIEW_COMMENT
|
||||
(details to be implemented)
|
||||
</div>,
|
||||
"title": "comment on review of #5: This pull request will be more contentious. I can feel it...",
|
||||
"type": "PULL_REQUEST_REVIEW_COMMENT",
|
||||
},
|
||||
Object {
|
||||
"id": Object {
|
||||
"id": "MDQ6VXNlcjE0MDAwMjM=",
|
||||
"type": "USER",
|
||||
},
|
||||
"payload": Object {
|
||||
"login": "dandelionmane",
|
||||
},
|
||||
"rendered": <div>
|
||||
type:
|
||||
USER
|
||||
(details to be implemented)
|
||||
</div>,
|
||||
"title": "dandelionmane",
|
||||
"type": "USER",
|
||||
},
|
||||
Object {
|
||||
"id": Object {
|
||||
"id": "MDQ6VXNlcjQzMTc4MDY=",
|
||||
"type": "USER",
|
||||
},
|
||||
"payload": Object {
|
||||
"login": "wchargin",
|
||||
},
|
||||
"rendered": <div>
|
||||
type:
|
||||
USER
|
||||
(details to be implemented)
|
||||
</div>,
|
||||
"title": "wchargin",
|
||||
"type": "USER",
|
||||
},
|
||||
Object {
|
||||
"id": Object {
|
||||
"id": "MDU6SXNzdWUzMDA5MzQ4MTg=",
|
||||
"type": "ISSUE",
|
||||
},
|
||||
"payload": Object {
|
||||
"body": "This is just an example issue.",
|
||||
"number": 1,
|
||||
"title": "An example issue.",
|
||||
},
|
||||
"rendered": <div>
|
||||
type:
|
||||
ISSUE
|
||||
(details to be implemented)
|
||||
</div>,
|
||||
"title": "#1: An example issue.",
|
||||
"type": "ISSUE",
|
||||
},
|
||||
Object {
|
||||
"id": Object {
|
||||
"id": "MDU6SXNzdWUzMDA5MzQ5ODA=",
|
||||
"type": "ISSUE",
|
||||
},
|
||||
"payload": Object {
|
||||
"body": "This issue references another issue, namely #1",
|
||||
"number": 2,
|
||||
"title": "A referencing issue.",
|
||||
},
|
||||
"rendered": <div>
|
||||
type:
|
||||
ISSUE
|
||||
(details to be implemented)
|
||||
</div>,
|
||||
"title": "#2: A referencing issue.",
|
||||
"type": "ISSUE",
|
||||
},
|
||||
Object {
|
||||
"id": Object {
|
||||
"id": "MDU6SXNzdWUzMDA5MzYzNzQ=",
|
||||
"type": "ISSUE",
|
||||
},
|
||||
"payload": Object {
|
||||
"body": "Alas, its life as an open issue had only just begun.",
|
||||
"number": 4,
|
||||
"title": "A closed pull request",
|
||||
},
|
||||
"rendered": <div>
|
||||
type:
|
||||
ISSUE
|
||||
(details to be implemented)
|
||||
</div>,
|
||||
"title": "#4: A closed pull request",
|
||||
"type": "ISSUE",
|
||||
},
|
||||
Object {
|
||||
"id": Object {
|
||||
"id": "MDU6SXNzdWUzMDU5OTM3NzM=",
|
||||
"type": "ISSUE",
|
||||
},
|
||||
"payload": Object {
|
||||
"body": "This issue shall shortly have a few comments.",
|
||||
"number": 6,
|
||||
"title": "An issue with comments",
|
||||
},
|
||||
"rendered": <div>
|
||||
type:
|
||||
ISSUE
|
||||
(details to be implemented)
|
||||
</div>,
|
||||
"title": "#6: An issue with comments",
|
||||
"type": "ISSUE",
|
||||
},
|
||||
Object {
|
||||
"id": Object {
|
||||
"id": "MDU6SXNzdWUzMDY5ODM1NTI=",
|
||||
"type": "ISSUE",
|
||||
},
|
||||
"payload": Object {
|
||||
"body": "Deal with this, naive string display algorithms!!!!!",
|
||||
"number": 7,
|
||||
"title": "An issue with an extremely long title, which even has a VerySuperFragicalisticialiManyCharacterUberLongTriplePlusGood word in it, and should really be truncated intelligently or something",
|
||||
},
|
||||
"rendered": <div>
|
||||
type:
|
||||
ISSUE
|
||||
(details to be implemented)
|
||||
</div>,
|
||||
"title": "#7: An issue with an extremely long title, which even has a VerySuperFragicalisticialiManyCharacterUberLongTriplePlusGood word in it, and should really be truncated intelligently or something",
|
||||
"type": "ISSUE",
|
||||
},
|
||||
Object {
|
||||
"id": Object {
|
||||
"id": "MDU6SXNzdWUzMDY5ODUzNjc=",
|
||||
"type": "ISSUE",
|
||||
},
|
||||
"payload": Object {
|
||||
"body": "Issue with Unicode: ȴሲ𣐳楢👍 :heart: 𐤔𐤁𐤀𐤑𐤍𐤉𐤔𐤌𐤄𐤍𐤍 ❤️
|
||||
Issue with Unicode: ȴሲ𣐳楢👍 :heart: 𐤔𐤁𐤀𐤑𐤍𐤉𐤔𐤌𐤄𐤍𐤍 ❤️",
|
||||
"number": 8,
|
||||
"title": "Issue with Unicode: ȴሲ𣐳楢👍 :heart: 𐤔𐤁𐤀𐤑𐤍𐤉𐤔𐤌𐤄𐤍𐤍 ❤️",
|
||||
},
|
||||
"rendered": <div>
|
||||
type:
|
||||
ISSUE
|
||||
(details to be implemented)
|
||||
</div>,
|
||||
"title": "#8: Issue with Unicode: ȴሲ𣐳楢👍 :heart: 𐤔𐤁𐤀𐤑𐤍𐤉𐤔𐤌𐤄𐤍𐤍 ❤️",
|
||||
"type": "ISSUE",
|
||||
},
|
||||
]
|
||||
`;
|
|
@ -0,0 +1,110 @@
|
|||
// @flow
|
||||
|
||||
import React from "react";
|
||||
|
||||
import {Graph} from "../../../../core/graph";
|
||||
import type {Node} from "../../../../core/graph";
|
||||
import type {
|
||||
NodePayload,
|
||||
EdgeID,
|
||||
NodeType,
|
||||
NodeTypes,
|
||||
IssueNodePayload,
|
||||
PullRequestNodePayload,
|
||||
CommentNodePayload,
|
||||
PullRequestReviewCommentNodePayload,
|
||||
PullRequestReviewNodePayload,
|
||||
AuthorNodePayload,
|
||||
} from "../../../github/githubPlugin";
|
||||
import type {PluginAdapter} from "../pluginAdapter";
|
||||
import {GITHUB_PLUGIN_NAME, getNodeType} from "../../../github/githubPlugin";
|
||||
|
||||
const adapter: PluginAdapter<NodePayload> = {
|
||||
pluginName: GITHUB_PLUGIN_NAME,
|
||||
|
||||
renderer: class GitHubNodeRenderer extends React.Component<{
|
||||
graph: Graph<any, any>,
|
||||
node: Node<NodePayload>,
|
||||
}> {
|
||||
render() {
|
||||
const type = adapter.extractType(this.props.graph, this.props.node);
|
||||
return <div>type: {type} (details to be implemented)</div>;
|
||||
}
|
||||
},
|
||||
|
||||
extractType(graph: *, node: Node<NodePayload>): NodeType {
|
||||
return getNodeType(node);
|
||||
},
|
||||
|
||||
extractTitle(graph: *, node: Node<NodePayload>): string {
|
||||
// NOTE: If the graph is malformed such that there are containment
|
||||
// cycles, then this function may blow the stack or fail to
|
||||
// terminate. (If necessary, we can fix this by tracking all
|
||||
// previously queried IDs.)
|
||||
function extractParentTitles(node: Node<NodePayload>): string[] {
|
||||
return graph
|
||||
.getInEdges(node.address)
|
||||
.filter((e) => {
|
||||
const edgeID: EdgeID = JSON.parse(e.address.id);
|
||||
return edgeID.type === "CONTAINMENT";
|
||||
})
|
||||
.map((e) => graph.getNode(e.src))
|
||||
.map((container) => {
|
||||
return adapter.extractTitle(graph, container);
|
||||
});
|
||||
}
|
||||
function extractIssueOrPrTitle(
|
||||
node: Node<IssueNodePayload | PullRequestNodePayload>
|
||||
) {
|
||||
return `#${node.payload.number}: ${node.payload.title}`;
|
||||
}
|
||||
function extractCommentTitle(
|
||||
kind: string,
|
||||
node: Node<CommentNodePayload | PullRequestReviewCommentNodePayload>
|
||||
) {
|
||||
const parentTitles = extractParentTitles(node);
|
||||
if (parentTitles.length === 0) {
|
||||
// Should never happen.
|
||||
return "comment (orphaned)";
|
||||
} else {
|
||||
// Should just be one parent.
|
||||
return `comment on ${parentTitles.join(" and ")}`;
|
||||
}
|
||||
}
|
||||
function extractPRReviewTitle(node: Node<PullRequestReviewNodePayload>) {
|
||||
const parentTitles = extractParentTitles(node);
|
||||
if (parentTitles.length === 0) {
|
||||
// Should never happen.
|
||||
return "pull request review (orphaned)";
|
||||
} else {
|
||||
// Should just be one parent.
|
||||
return `review of ${parentTitles.join(" and ")}`;
|
||||
}
|
||||
}
|
||||
function extractAuthorTitle(node: Node<AuthorNodePayload>) {
|
||||
return node.payload.login;
|
||||
}
|
||||
type TypedNodeToStringExtractor = <T: $Values<NodeTypes>>(
|
||||
T
|
||||
) => (node: Node<$ElementType<T, "payload">>) => string;
|
||||
const extractors: $Exact<$ObjMap<NodeTypes, TypedNodeToStringExtractor>> = {
|
||||
ISSUE: extractIssueOrPrTitle,
|
||||
PULL_REQUEST: extractIssueOrPrTitle,
|
||||
COMMENT: (node) => extractCommentTitle("comment", node),
|
||||
PULL_REQUEST_REVIEW_COMMENT: (node) =>
|
||||
extractCommentTitle("review comment", node),
|
||||
PULL_REQUEST_REVIEW: extractPRReviewTitle,
|
||||
USER: extractAuthorTitle,
|
||||
ORGANIZATION: extractAuthorTitle,
|
||||
BOT: extractAuthorTitle,
|
||||
};
|
||||
function fallbackAccessor(node: Node<NodePayload>) {
|
||||
throw new Error(`unknown node type: ${getNodeType(node)}`);
|
||||
}
|
||||
return (extractors[getNodeType(node)] || fallbackAccessor)(
|
||||
(node: Node<any>)
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export default adapter;
|
|
@ -0,0 +1,36 @@
|
|||
// @flow
|
||||
|
||||
import React from "react";
|
||||
import reactTestRenderer from "react-test-renderer";
|
||||
import stringify from "json-stable-stringify";
|
||||
|
||||
import type {NodeID} from "../../../github/githubPlugin";
|
||||
import {GithubParser} from "../../../github/githubPlugin";
|
||||
import exampleRepoData from "../../../github/demoData/example-repo.json";
|
||||
import adapter from "./githubPluginAdapter";
|
||||
|
||||
describe("githubPluginAdapter", () => {
|
||||
it("operates on the example repo", () => {
|
||||
const parser = new GithubParser("sourcecred/example-repo");
|
||||
parser.addData(exampleRepoData.data);
|
||||
const graph = parser.graph;
|
||||
|
||||
const result = graph
|
||||
.getAllNodes()
|
||||
.map((node) => ({
|
||||
id: (JSON.parse(node.address.id): NodeID),
|
||||
payload: node.payload,
|
||||
type: adapter.extractType(graph, node),
|
||||
title: adapter.extractTitle(graph, node),
|
||||
rendered: reactTestRenderer.create(
|
||||
<adapter.renderer graph={graph} node={node} />
|
||||
),
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
const ka = stringify(a.id);
|
||||
const kb = stringify(b.id);
|
||||
return ka > kb ? 1 : ka < kb ? -1 : 0;
|
||||
});
|
||||
expect(result).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,13 @@
|
|||
// @flow
|
||||
|
||||
import type {Graph, Node} from "../../../core/graph";
|
||||
import type {ComponentType} from "react";
|
||||
|
||||
export interface PluginAdapter<-NodePayload> {
|
||||
pluginName: string;
|
||||
renderer: $Subtype<
|
||||
ComponentType<{graph: Graph<any, any>, node: Node<NodePayload>}>
|
||||
>;
|
||||
extractType(graph: Graph<any, any>, node: Node<NodePayload>): string;
|
||||
extractTitle(graph: Graph<any, any>, node: Node<NodePayload>): string;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
// @flow
|
||||
|
||||
import {AdapterSet} from "./adapterSet";
|
||||
import githubPluginAdapter from "./adapters/githubPluginAdapter";
|
||||
|
||||
const adapterSet = new AdapterSet();
|
||||
adapterSet.addAdapter(githubPluginAdapter);
|
||||
|
||||
export default adapterSet;
|
|
@ -5452,6 +5452,14 @@ react-error-overlay@^4.0.0:
|
|||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-4.0.0.tgz#d198408a85b4070937a98667f500c832f86bd5d4"
|
||||
|
||||
react-test-renderer@^16.2.0:
|
||||
version "16.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.2.0.tgz#bddf259a6b8fcd8555f012afc8eacc238872a211"
|
||||
dependencies:
|
||||
fbjs "^0.8.16"
|
||||
object-assign "^4.1.1"
|
||||
prop-types "^15.6.0"
|
||||
|
||||
react@^16.2.0:
|
||||
version "16.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-16.2.0.tgz#a31bd2dab89bff65d42134fa187f24d054c273ba"
|
||||
|
|
Loading…
Reference in New Issue