Reorganize GitHub porcelain tests (#260)

This re-organizes the GitHub porcelain tests to be:
- organized by each method signature, rather than having blocks that
test many different methods on each wrapper
- make extensive use of snapshot tests for convenience

Test plan: Inspect the new unit tests, and the corresponding snapshots.
It should be relatively easy to do this because you can copy+paste the
urls to verify the properties.
This commit is contained in:
Dandelion Mané 2018-05-10 12:53:32 -07:00 committed by GitHub
parent 47bec6cc10
commit 2a88bbc091
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 329 additions and 190 deletions

View File

@ -1,66 +1,175 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`GitHub porcelain has wrappers for Authors 1`] = `2`;
exports[`GitHub porcelain has wrappers for Authors 2`] = `
exports[`GitHub porcelain all wrappers provide a type() method 1`] = `
Object {
"address": Object {
"id": "https://github.com/decentralion",
"pluginName": "sourcecred/github-beta",
"type": "AUTHOR",
},
"payload": Object {
"login": "decentralion",
"subtype": "USER",
"url": "https://github.com/decentralion",
},
"https://github.com/decentralion": "AUTHOR",
"https://github.com/sourcecred/example-github/issues/2": "ISSUE",
"https://github.com/sourcecred/example-github/issues/2#issuecomment-373768703": "COMMENT",
"https://github.com/sourcecred/example-github/pull/5": "PULL_REQUEST",
"https://github.com/sourcecred/example-github/pull/5#discussion_r171460198": "PULL_REQUEST_REVIEW_COMMENT",
"https://github.com/sourcecred/example-github/pull/5#pullrequestreview-100313899": "PULL_REQUEST_REVIEW",
}
`;
exports[`GitHub porcelain has wrappers for Comments 1`] = `3`;
exports[`GitHub porcelain has wrappers for Comments 2`] = `
exports[`GitHub porcelain all wrappers provide a url() method 1`] = `
Object {
"address": Object {
"id": "https://github.com/sourcecred/example-github/issues/6#issuecomment-373768442",
"pluginName": "sourcecred/github-beta",
"type": "COMMENT",
},
"payload": Object {
"body": "A wild COMMENT appeared!",
"url": "https://github.com/sourcecred/example-github/issues/6#issuecomment-373768442",
},
"https://github.com/decentralion": "https://github.com/decentralion",
"https://github.com/sourcecred/example-github/issues/2": "https://github.com/sourcecred/example-github/issues/2",
"https://github.com/sourcecred/example-github/issues/2#issuecomment-373768703": "https://github.com/sourcecred/example-github/issues/2#issuecomment-373768703",
"https://github.com/sourcecred/example-github/pull/5": "https://github.com/sourcecred/example-github/pull/5",
"https://github.com/sourcecred/example-github/pull/5#discussion_r171460198": "https://github.com/sourcecred/example-github/pull/5#discussion_r171460198",
"https://github.com/sourcecred/example-github/pull/5#pullrequestreview-100313899": "https://github.com/sourcecred/example-github/pull/5#pullrequestreview-100313899",
}
`;
exports[`GitHub porcelain has wrappers for Issues 1`] = `
exports[`GitHub porcelain issues and pull requests have comments 1`] = `
Object {
"address": Object {
"id": "https://github.com/sourcecred/example-github/issues/1",
"pluginName": "sourcecred/github-beta",
"type": "ISSUE",
},
"payload": Object {
"body": "This is just an example issue.",
"number": 1,
"title": "An example issue.",
"url": "https://github.com/sourcecred/example-github/issues/1",
},
"https://github.com/sourcecred/example-github/issues/1": Array [],
"https://github.com/sourcecred/example-github/issues/2": Array [
"https://github.com/sourcecred/example-github/issues/2#issuecomment-373768703",
"https://github.com/sourcecred/example-github/issues/2#issuecomment-373768850",
"https://github.com/sourcecred/example-github/issues/2#issuecomment-385576185",
"https://github.com/sourcecred/example-github/issues/2#issuecomment-385576220",
"https://github.com/sourcecred/example-github/issues/2#issuecomment-385576248",
"https://github.com/sourcecred/example-github/issues/2#issuecomment-385576273",
"https://github.com/sourcecred/example-github/issues/2#issuecomment-385576920",
"https://github.com/sourcecred/example-github/issues/2#issuecomment-385576936",
],
"https://github.com/sourcecred/example-github/issues/4": Array [],
"https://github.com/sourcecred/example-github/issues/6": Array [
"https://github.com/sourcecred/example-github/issues/6#issuecomment-373768442",
"https://github.com/sourcecred/example-github/issues/6#issuecomment-373768538",
"https://github.com/sourcecred/example-github/issues/6#issuecomment-385223316",
],
"https://github.com/sourcecred/example-github/issues/7": Array [],
"https://github.com/sourcecred/example-github/issues/8": Array [],
"https://github.com/sourcecred/example-github/pull/3": Array [
"https://github.com/sourcecred/example-github/pull/3#issuecomment-369162222",
],
"https://github.com/sourcecred/example-github/pull/5": Array [],
"https://github.com/sourcecred/example-github/pull/9": Array [],
}
`;
exports[`GitHub porcelain has wrappers for PullRequests Merged 1`] = `
exports[`GitHub porcelain issues and pull requests have numbers 1`] = `
Object {
"address": Object {
"id": "https://github.com/sourcecred/example-github/pull/3",
"pluginName": "sourcecred/github-beta",
"type": "PULL_REQUEST",
},
"payload": Object {
"body": "Oh look, it's a pull request.",
"number": 3,
"title": "Add README, merge via PR.",
"url": "https://github.com/sourcecred/example-github/pull/3",
},
"https://github.com/sourcecred/example-github/issues/1": 1,
"https://github.com/sourcecred/example-github/issues/2": 2,
"https://github.com/sourcecred/example-github/issues/4": 4,
"https://github.com/sourcecred/example-github/issues/6": 6,
"https://github.com/sourcecred/example-github/issues/7": 7,
"https://github.com/sourcecred/example-github/issues/8": 8,
"https://github.com/sourcecred/example-github/pull/3": 3,
"https://github.com/sourcecred/example-github/pull/5": 5,
"https://github.com/sourcecred/example-github/pull/9": 9,
}
`;
exports[`GitHub porcelain issues and pull requests have titles 1`] = `
Object {
"https://github.com/sourcecred/example-github/issues/1": "An example issue.",
"https://github.com/sourcecred/example-github/issues/2": "A referencing issue.",
"https://github.com/sourcecred/example-github/issues/4": "A closed pull request",
"https://github.com/sourcecred/example-github/issues/6": "An issue with comments",
"https://github.com/sourcecred/example-github/issues/7": "An issue with an extremely long title, which even has a VerySuperFragicalisticialiManyCharacterUberLongTriplePlusGood word in it, and should really be truncated intelligently or something",
"https://github.com/sourcecred/example-github/issues/8": "Issue with Unicode: ȴሲ𣐳楢👍 :heart: 𐤔𐤁𐤀𐤑𐤍𐤉𐤔𐤌𐤄𐤍𐤍 ❤️",
"https://github.com/sourcecred/example-github/pull/3": "Add README, merge via PR.",
"https://github.com/sourcecred/example-github/pull/5": "This pull request will be more contentious. I can feel it...",
"https://github.com/sourcecred/example-github/pull/9": "An unmerged pull request",
}
`;
exports[`GitHub porcelain posts have authors 1`] = `
Object {
"https://github.com/sourcecred/example-github/issues/2": Array [
"decentralion",
],
"https://github.com/sourcecred/example-github/issues/2#issuecomment-373768703": Array [
"decentralion",
],
"https://github.com/sourcecred/example-github/pull/5": Array [
"decentralion",
],
"https://github.com/sourcecred/example-github/pull/5#discussion_r171460198": Array [
"wchargin",
],
"https://github.com/sourcecred/example-github/pull/5#pullrequestreview-100313899": Array [
"wchargin",
],
}
`;
exports[`GitHub porcelain posts have bodies 1`] = `
Object {
"https://github.com/sourcecred/example-github/issues/2": "This issue references another issue, namely #1",
"https://github.com/sourcecred/example-github/issues/2#issuecomment-373768703": "It should also be possible to reference by exact url: https://github.com/sourcecred/example-github/issues/6",
"https://github.com/sourcecred/example-github/pull/5": "@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",
"https://github.com/sourcecred/example-github/pull/5#discussion_r171460198": "seems a bit capricious",
"https://github.com/sourcecred/example-github/pull/5#pullrequestreview-100313899": "hmmm.jpg",
}
`;
exports[`GitHub porcelain posts have parents 1`] = `
Object {
"https://github.com/sourcecred/example-github/issues/2": "https://github.com/sourcecred/example-github",
"https://github.com/sourcecred/example-github/issues/2#issuecomment-373768703": "https://github.com/sourcecred/example-github/issues/2",
"https://github.com/sourcecred/example-github/pull/5": "https://github.com/sourcecred/example-github",
"https://github.com/sourcecred/example-github/pull/5#discussion_r171460198": "https://github.com/sourcecred/example-github/pull/5#pullrequestreview-100313899",
"https://github.com/sourcecred/example-github/pull/5#pullrequestreview-100313899": "https://github.com/sourcecred/example-github/pull/5",
}
`;
exports[`GitHub porcelain posts have references 1`] = `
Object {
"https://github.com/sourcecred/example-github/issues/2": Array [
"https://github.com/sourcecred/example-github/issues/1",
],
"https://github.com/sourcecred/example-github/issues/2#issuecomment-373768703": Array [
"https://github.com/sourcecred/example-github/issues/6",
],
"https://github.com/sourcecred/example-github/pull/5": Array [
"https://github.com/wchargin",
],
"https://github.com/sourcecred/example-github/pull/5#discussion_r171460198": Array [],
"https://github.com/sourcecred/example-github/pull/5#pullrequestreview-100313899": Array [],
}
`;
exports[`GitHub porcelain pull request reviews have review comments 1`] = `
Object {
"https://github.com/sourcecred/example-github/pull/5#pullrequestreview-100313899": Array [
"https://github.com/sourcecred/example-github/pull/5#discussion_r171460198",
],
"https://github.com/sourcecred/example-github/pull/5#pullrequestreview-100314038": Array [],
}
`;
exports[`GitHub porcelain pull request reviews have states 1`] = `
Object {
"https://github.com/sourcecred/example-github/pull/5#pullrequestreview-100313899": "CHANGES_REQUESTED",
"https://github.com/sourcecred/example-github/pull/5#pullrequestreview-100314038": "APPROVED",
}
`;
exports[`GitHub porcelain pull requests have mergeCommitHashes 1`] = `
Object {
"https://github.com/sourcecred/example-github/pull/3": "0a223346b4e6dec0127b1e6aa892c4ee0424b66a",
"https://github.com/sourcecred/example-github/pull/5": "6d5b3aa31ebb68a06ceb46bbd6cf49b6ccd6f5e6",
"https://github.com/sourcecred/example-github/pull/9": null,
}
`;
exports[`GitHub porcelain pull requests have reviews 1`] = `
Object {
"https://github.com/sourcecred/example-github/pull/3": Array [],
"https://github.com/sourcecred/example-github/pull/5": Array [
"https://github.com/sourcecred/example-github/pull/5#pullrequestreview-100313899",
"https://github.com/sourcecred/example-github/pull/5#pullrequestreview-100314038",
],
"https://github.com/sourcecred/example-github/pull/9": Array [],
}
`;

View File

@ -3,12 +3,15 @@
import type {Address} from "../../core/address";
import {parse} from "./parser";
import exampleRepoData from "./demoData/example-github.json";
import type {Entity} from "./porcelain";
import {
asEntity,
Porcelain,
Repository,
Issue,
PullRequest,
PullRequestReview,
PullRequestReviewComment,
Comment,
Author,
} from "./porcelain";
@ -27,6 +30,21 @@ describe("GitHub porcelain", () => {
const graph = parse(exampleRepoData);
const porcelain = new Porcelain(graph);
const repo = porcelain.repository("sourcecred", "example-github");
function expectPropertiesToMatchSnapshot<T: Entity>(
entities: $ReadOnlyArray<T>,
extractor: (T) => mixed
) {
const urlToProperty = {};
entities.forEach((e) => {
if (e.url() in urlToProperty) {
throw new Error(`Duplicate url: ${e.url()}`);
}
urlToProperty[e.url()] = extractor(e);
});
expect(urlToProperty).toMatchSnapshot();
}
function issueOrPRByNumber(n: number): Issue | PullRequest {
const result = repo.issueOrPRByNumber(n);
if (result == null) {
@ -34,9 +52,159 @@ describe("GitHub porcelain", () => {
}
return result;
}
const issue = issueOrPRByNumber(2);
const comment = issue.comments()[0];
const pullRequest = PullRequest.from(issueOrPRByNumber(5));
const pullRequestReview = pullRequest.reviews()[0];
const pullRequestReviewComment = pullRequestReview.comments()[0];
const author = issue.authors()[0];
const allWrappers = [
issue,
pullRequest,
comment,
pullRequestReview,
pullRequestReviewComment,
author,
];
it("all wrappers provide a type() method", () => {
expectPropertiesToMatchSnapshot(allWrappers, (e) => e.type());
});
it("all wrappers provide a url() method", () => {
expectPropertiesToMatchSnapshot(allWrappers, (e) => e.url());
});
it("all wrappers provide an address() method", () => {
allWrappers.forEach((w) => {
const addr = w.address();
const url = w.url();
const type = w.type();
expect(addr.id).toBe(url);
expect(addr.type).toBe(type);
expect(addr.pluginName).toBe(PLUGIN_NAME);
});
});
it("all wrappers provide a node() method", () => {
allWrappers.forEach((w) => {
const node = w.node();
const addr = w.address();
expect(node.address).toEqual(addr);
});
});
describe("type verifiers", () => {
it("are provided by all wrappers", () => {
// Check each one individually to verify the flowtypes
const _unused_repo: Repository = Repository.from(repo);
const _unused_issue: Issue = Issue.from(issue);
const _unused_pullRequest: PullRequest = PullRequest.from(pullRequest);
const _unused_comment: Comment = Comment.from(comment);
const _unused_pullRequestReview: PullRequestReview = PullRequestReview.from(
pullRequestReview
);
const _unused_pullRequestReviewComment: PullRequestReviewComment = PullRequestReviewComment.from(
pullRequestReviewComment
);
const _unused_author: Author = Author.from(author);
// Check them programatically so that if we add another wrapper, we can't forget to update.
allWrappers.forEach((e) => {
expect(e.constructor.from(e)).toEqual(e);
});
});
it("and errors are thrown when used incorrectly", () => {
expect(() => Repository.from(issue)).toThrowError("to have type");
expect(() => Issue.from(repo)).toThrowError("to have type");
expect(() => Comment.from(repo)).toThrowError("to have type");
expect(() => PullRequest.from(repo)).toThrowError("to have type");
expect(() => PullRequestReview.from(repo)).toThrowError("to have type");
expect(() => PullRequestReviewComment.from(repo)).toThrowError(
"to have type"
);
expect(() => Author.from(repo)).toThrowError("to have type");
});
});
describe("posts", () => {
const allPosts = [
issue,
pullRequest,
pullRequestReview,
pullRequestReviewComment,
comment,
];
it("have parents", () => {
expectPropertiesToMatchSnapshot(allPosts, (e) => e.parent().url());
});
it("have bodies", () => {
expectPropertiesToMatchSnapshot(allPosts, (e) => e.body());
});
it("have authors", () => {
expectPropertiesToMatchSnapshot(allPosts, (e) =>
e.authors().map((a) => a.login())
);
});
it("have references", () => {
expectPropertiesToMatchSnapshot(allPosts, (e) =>
e.references().map((r) => r.url())
);
});
});
describe("issues and pull requests", () => {
const issuesAndPRs = [1, 2, 3, 4, 5, 6, 7, 8, 9].map((n) =>
issueOrPRByNumber(n)
);
it("have numbers", () => {
expectPropertiesToMatchSnapshot(issuesAndPRs, (e) => e.number());
});
it("have titles", () => {
expectPropertiesToMatchSnapshot(issuesAndPRs, (e) => e.title());
});
it("have comments", () => {
expectPropertiesToMatchSnapshot(issuesAndPRs, (e) =>
e.comments().map((c) => c.url())
);
});
});
describe("pull requests", () => {
const prs = [
PullRequest.from(issueOrPRByNumber(3)),
PullRequest.from(issueOrPRByNumber(5)),
PullRequest.from(issueOrPRByNumber(9)),
];
it("have mergeCommitHashes", () => {
expectPropertiesToMatchSnapshot(prs, (e) => e.mergeCommitHash());
});
it("have reviews", () => {
expectPropertiesToMatchSnapshot(prs, (e) =>
e.reviews().map((r) => r.url())
);
});
});
describe("pull request reviews", () => {
const reviews = pullRequest.reviews();
it("have review comments", () => {
expectPropertiesToMatchSnapshot(reviews, (e) =>
e.comments().map((e) => e.url())
);
});
it("have states", () => {
expectPropertiesToMatchSnapshot(reviews, (e) => e.state());
});
});
describe("asEntity", () => {
// Note: In the "wrappers" block, we test that the asEntity method works
// for each wrapper type. Here, we just test that it fails as expected.
it("works for each wrapper", () => {
allWrappers.forEach((w) => {
expect(asEntity(w.graph, w.address())).toEqual(w);
});
});
it("errors when given an address with the wrong plugin name", () => {
const addr: Address = {
pluginName: "the magnificent foo plugin",
@ -54,6 +222,7 @@ describe("GitHub porcelain", () => {
expect(() => asEntity(graph, addr)).toThrow("invalid type");
});
});
describe("has repository finding", () => {
it("which works for an existing repository", () => {
expect(porcelain.repository("sourcecred", "example-github")).toEqual(
@ -66,145 +235,6 @@ describe("GitHub porcelain", () => {
});
});
describe("has wrappers for", () => {
it("Repositories", () => {
expect(repo.url()).toBe("https://github.com/sourcecred/example-github");
expect(repo.owner()).toBe("sourcecred");
expect(repo.name()).toBe("example-github");
expect(repo).toEqual(asEntity(graph, repo.address()));
});
it("Issues", () => {
const issue = issueOrPRByNumber(1);
expect(issue.title()).toBe("An example issue.");
expect(issue.body()).toBe("This is just an example issue.");
expect(issue.number()).toBe(1);
expect(issue.type()).toBe(ISSUE_NODE_TYPE);
expect(issue.url()).toBe(
"https://github.com/sourcecred/example-github/issues/1"
);
expect(issue.node()).toMatchSnapshot();
expect(issue.address()).toEqual(issue.node().address);
expect(issue.authors().map((x) => x.login())).toEqual(["decentralion"]);
expect(issue).toEqual(asEntity(graph, issue.address()));
expect(issue.parent()).toEqual(repo);
});
describe("PullRequests", () => {
it("Merged", () => {
const pullRequest = PullRequest.from(issueOrPRByNumber(3));
expect(pullRequest.body()).toBe("Oh look, it's a pull request.");
expect(pullRequest.title()).toBe("Add README, merge via PR.");
expect(pullRequest.url()).toBe(
"https://github.com/sourcecred/example-github/pull/3"
);
expect(pullRequest.number()).toBe(3);
expect(pullRequest.type()).toBe(PULL_REQUEST_NODE_TYPE);
expect(pullRequest.node()).toMatchSnapshot();
expect(pullRequest.address()).toEqual(pullRequest.node().address);
expect(pullRequest.mergeCommitHash()).toEqual(
"0a223346b4e6dec0127b1e6aa892c4ee0424b66a"
);
expect(pullRequest).toEqual(asEntity(graph, pullRequest.address()));
expect(pullRequest.parent()).toEqual(repo);
});
it("Unmerged", () => {
const pullRequest = PullRequest.from(issueOrPRByNumber(9));
expect(pullRequest.mergeCommitHash()).toEqual(null);
});
});
it("Pull Request Reviews", () => {
const pr = PullRequest.from(issueOrPRByNumber(5));
const reviews = pr.reviews();
expect(reviews).toHaveLength(2);
expect(reviews[0].state()).toBe("CHANGES_REQUESTED");
expect(reviews[1].state()).toBe("APPROVED");
expect(reviews[0]).toEqual(asEntity(graph, reviews[0].address()));
expect(reviews[0].parent()).toEqual(pr);
});
it("Pull Request Review Comments", () => {
const pr = PullRequest.from(issueOrPRByNumber(5));
const reviews = pr.reviews();
expect(reviews).toHaveLength(2);
const comments = reviews[0].comments();
expect(comments).toHaveLength(1);
const comment = comments[0];
expect(comment.parent()).toEqual(reviews[0]);
expect(comment.url()).toBe(
"https://github.com/sourcecred/example-github/pull/5#discussion_r171460198"
);
expect(comment.body()).toBe("seems a bit capricious");
expect(comment.authors().map((a) => a.login())).toEqual(["wchargin"]);
expect(comment).toEqual(asEntity(graph, comment.address()));
});
it("Comments", () => {
const issue = issueOrPRByNumber(6);
const comments = issue.comments();
expect(comments.length).toMatchSnapshot();
const comment = comments[0];
expect(comment.type()).toBe(COMMENT_NODE_TYPE);
expect(comment.body()).toBe("A wild COMMENT appeared!");
expect(comment.url()).toBe(
"https://github.com/sourcecred/example-github/issues/6#issuecomment-373768442"
);
expect(comment.node()).toMatchSnapshot();
expect(comment.address()).toEqual(comment.node().address);
expect(comment.authors().map((x) => x.login())).toEqual(["decentralion"]);
expect(comment).toEqual(asEntity(graph, comment.address()));
expect(comment.parent()).toEqual(issue);
});
it("Authors", () => {
const authors = porcelain.authors();
// So we don't need to manually update the test if a new person posts
expect(authors.length).toMatchSnapshot();
const decentralion = authors.find((x) => x.login() === "decentralion");
if (decentralion == null) {
throw new Error("Who let the lions out?");
}
expect(decentralion.url()).toBe("https://github.com/decentralion");
expect(decentralion.type()).toBe(AUTHOR_NODE_TYPE);
expect(decentralion.subtype()).toBe("USER");
expect(decentralion.node()).toMatchSnapshot();
expect(decentralion.address()).toEqual(decentralion.node().address);
expect(decentralion).toEqual(asEntity(graph, decentralion.address()));
});
});
describe("has type coercion that", () => {
it("allows refining types when correct", () => {
const _unused_repo: Repository = Repository.from(repo);
const _unused_issue: Issue = Issue.from(issueOrPRByNumber(1));
const _unused_pr: PullRequest = PullRequest.from(issueOrPRByNumber(3));
const _unused_author: Author = Author.from(
issueOrPRByNumber(3).authors()[0]
);
const _unused_comment: Comment = Comment.from(
issueOrPRByNumber(2).comments()[0]
);
});
it("throws an error on bad type refinement", () => {
expect(() => Repository.from(issueOrPRByNumber(1))).toThrowError(
"to have type REPOSITORY"
);
expect(() => PullRequest.from(issueOrPRByNumber(1))).toThrowError(
"to have type PULL_REQUEST"
);
expect(() => Issue.from(issueOrPRByNumber(3))).toThrowError(
"to have type ISSUE"
);
expect(() =>
Comment.from(issueOrPRByNumber(3).authors()[0])
).toThrowError("to have type COMMENT");
expect(() =>
Author.from(issueOrPRByNumber(2).comments()[0])
).toThrowError("to have type AUTHOR");
});
});
describe("References", () => {
it("via #-number", () => {
const srcIssue = issueOrPRByNumber(2);