From 6a4b385fa84b8c69b10a391ada2265956bc97242 Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Fri, 23 Apr 2021 13:43:24 -0700 Subject: [PATCH] Update Chirp spec to mock the HTTP request --- app/javascript/components/Chirp.js | 2 +- app/javascript/components/Chirp.spec.js | 96 +++++++++ .../__snapshots__/Chirp.spec.js.snap | 192 ++++++++++++++++++ package.json | 2 + yarn.lock | 45 ++++ 5 files changed, 336 insertions(+), 1 deletion(-) diff --git a/app/javascript/components/Chirp.js b/app/javascript/components/Chirp.js index 350f805..cb6b62f 100644 --- a/app/javascript/components/Chirp.js +++ b/app/javascript/components/Chirp.js @@ -1,5 +1,6 @@ import * as React from 'react'; import PropTypes from 'prop-types'; +import document from 'global/document'; import UserLink from './UserLink'; import RelativeDateTime from './RelativeDateTime'; @@ -71,7 +72,6 @@ export default class Chirp extends React.PureComponent { return response.json(); } this.setState({ liking: false }); - }) .then((chirp) => { this.setState({ diff --git a/app/javascript/components/Chirp.spec.js b/app/javascript/components/Chirp.spec.js index f866946..b7a4d99 100644 --- a/app/javascript/components/Chirp.spec.js +++ b/app/javascript/components/Chirp.spec.js @@ -1,5 +1,7 @@ /* eslint-env jest */ +import { enableFetchMocks } from 'jest-fetch-mock' + import React from 'react'; import renderer from 'react-test-renderer'; import Chirp from './Chirp'; @@ -7,6 +9,15 @@ import Chirp from './Chirp'; // RelativeDateTime changes based on the current time, // mock it here and we'll test that in its own specs. jest.mock('./RelativeDateTime', () => 'RelativeDateTime'); +jest.mock('global/document', () => ({ + querySelector(selector) { + if (selector === 'meta[name="csrf-token"]') { + return { content: 'b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c' }; + } else { + throw new Error('Unexpected queryselector in mocking area'); + } + } +})); describe('mention tracking', () => { it('links to mentioned users', () => { @@ -62,3 +73,88 @@ describe('mention tracking', () => { expect(tree).toMatchSnapshot(); }); }); + +describe('Like button', () => { + beforeEach(() => { + enableFetchMocks(); + }); + + it('sends a like request to the API', () => { + fetch.mockResponse((request) => ( + request.url === '/chirps/1/like.json' + ? ( + Promise.resolve(JSON.stringify({ + id: 1, + author: { id: 1, + name: "alice", + url: "/users/1" }, + content: "hey @bob @bob @carol @bob @bob", + created_at: "2021-04-22T01:08:10Z", + mentions: [ + { id: 2, + name: "bob", + url: "/users/2" }, + { id: 3, + name: "carol", + url: "/users/3" } + ], + updated_at: "2021-04-22T01:08:10Z", + likes_count: 1, + liked: true, + like_url: "/chirps/1/like" + })) + ) + : Promise.reject(new Error('Unexpected URL')) + )); + + const component = renderer.create( + + ); + + const treeBefore = component.toJSON(); + expect(treeBefore).toMatchSnapshot(); + + const likeButton = component.root.find((node) => ( + node.type === 'a' && node.props.href === '/chirps/1/like' + )); + + // Simulate a click event + likeButton.props.onClick({ preventDefault: jest.fn() }); + + expect(fetch.mock.calls.length).toEqual(1); + expect(fetch.mock.calls[0][0]).toEqual('/chirps/1/like.json') + + const treeAfter = component.toJSON(); + expect(treeAfter).toMatchSnapshot(); + + // We need to give React a moment to re-render the component + return new Promise((resolve) => { + setTimeout(() => { + const treeAfterRefresh = component.toJSON(); + expect(treeAfterRefresh).toMatchSnapshot(); + resolve(); + }, 0); + }); + }); +}); diff --git a/app/javascript/components/__snapshots__/Chirp.spec.js.snap b/app/javascript/components/__snapshots__/Chirp.spec.js.snap index 420664b..e4f4d82 100644 --- a/app/javascript/components/__snapshots__/Chirp.spec.js.snap +++ b/app/javascript/components/__snapshots__/Chirp.spec.js.snap @@ -1,5 +1,197 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Like button sends a like request to the API 1`] = ` +
+

+ hey + + @ + bob + + + + @ + bob + + + + @ + carol + + + + @ + bob + + + + @ + bob + + +

+ -- + + @ + alice + + , + + • + 0 likes + • + + Like + +
+`; + +exports[`Like button sends a like request to the API 2`] = ` +
+

+ hey + + @ + bob + + + + @ + bob + + + + @ + carol + + + + @ + bob + + + + @ + bob + + +

+ -- + + @ + alice + + , + + • + 0 likes + • + + Like + +
+`; + +exports[`Like button sends a like request to the API 3`] = ` +
+

+ hey + + @ + bob + + + + @ + bob + + + + @ + carol + + + + @ + bob + + + + @ + bob + + +

+ -- + + @ + alice + + , + + • + 1 like + • + + Unlike + +
+`; + exports[`mention tracking avoids things that look like email addresses 1`] = `

diff --git a/package.json b/package.json index e040af3..8089af5 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "@rails/activestorage": "^6.0.0", "@rails/ujs": "^6.0.0", "@rails/webpacker": "^5.0.0", + "global": "^4.4.0", "luxon": "^1.26.0", "prop-types": "^15.7.2", "react": "^17.0.2", @@ -27,6 +28,7 @@ "eslint-plugin-import": "^2.22.1", "eslint-plugin-react": "^7.23.2", "jest": "^26.6.3", + "jest-fetch-mock": "^3.0.3", "react-test-renderer": "^17.0.2", "webpack-dev-server": "^3.10.3" } diff --git a/yarn.lock b/yarn.lock index 0ebaa5c..d621a1f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2899,6 +2899,13 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" +cross-fetch@^3.0.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39" + integrity sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ== + dependencies: + node-fetch "2.6.1" + cross-spawn@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982" @@ -3384,6 +3391,11 @@ dom-serializer@0: domelementtype "^2.0.1" entities "^2.0.0" +dom-walk@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" + integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== + domain-browser@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" @@ -4360,6 +4372,14 @@ global-prefix@^3.0.0: kind-of "^6.0.2" which "^1.3.1" +global@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" + integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== + dependencies: + min-document "^2.19.0" + process "^0.11.10" + globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -5335,6 +5355,14 @@ jest-environment-node@^26.6.2: jest-mock "^26.6.2" jest-util "^26.6.2" +jest-fetch-mock@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/jest-fetch-mock/-/jest-fetch-mock-3.0.3.tgz#31749c456ae27b8919d69824f1c2bd85fe0a1f3b" + integrity sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw== + dependencies: + cross-fetch "^3.0.4" + promise-polyfill "^8.1.3" + jest-get-type@^26.3.0: version "26.3.0" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" @@ -6139,6 +6167,13 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +min-document@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU= + dependencies: + dom-walk "^0.1.0" + mini-css-extract-plugin@^0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz#47f2cf07aa165ab35733b1fc97d4c46c0564339e" @@ -6330,6 +6365,11 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== +node-fetch@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + node-forge@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" @@ -7746,6 +7786,11 @@ promise-inflight@^1.0.1: resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= +promise-polyfill@^8.1.3: + version "8.2.0" + resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-8.2.0.tgz#367394726da7561457aba2133c9ceefbd6267da0" + integrity sha512-k/TC0mIcPVF6yHhUvwAp7cvL6I2fFV7TzF1DuGPI8mBh4QQazf36xCKEHKTZKRysEoTQoQdKyP25J8MPJp7j5g== + prompts@^2.0.1: version "2.4.1" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.1.tgz#befd3b1195ba052f9fd2fde8a486c4e82ee77f61"