{"id":1415,"date":"2020-08-14T16:46:54","date_gmt":"2020-08-14T16:46:54","guid":{"rendered":"https:\/\/lvboard.infostore.in.ua\/?p=1415"},"modified":"2020-08-14T16:46:54","modified_gmt":"2020-08-14T16:46:54","slug":"full-guide-to-testing-javascript-react-with-jest-react-testing-library-cypress","status":"publish","type":"post","link":"https:\/\/lvboard.infostore.in.ua\/?p=1415","title":{"rendered":"Full Guide to Testing JavaScript &#038; React with Jest, React Testing Library, Cypress"},"content":{"rendered":"\n<p>Want to have more confidence in your codebase when making changes? Let\u2019s look at the modern ways and tools used to test JavaScript, TypeScript &amp; React.<\/p>\n\n\n\n<!--more-->\n\n\n\n<h2 id=\"-types-of-tests\"><a href=\"https:\/\/www.rrebase.com\/posts\/full-guide-to-testing-javascript-react#-types-of-tests\"><\/a>? Types of Tests<\/h2>\n\n\n\n<p>It\u2019s possible to divide tests into four different types. The distinction between these different types can be blurry. Thus many developers have a slightly different idea of what each type of test should exactly mean.<\/p>\n\n\n\n<h3 id=\"1-unit-tests\"><a href=\"https:\/\/www.rrebase.com\/posts\/full-guide-to-testing-javascript-react#1-unit-tests\"><\/a>1. Unit tests<\/h3>\n\n\n\n<p>Test the functionality of small isolated pieces of code (Jest, React Testing Library).<\/p>\n\n\n\n<h4 id=\"pros\"><a href=\"https:\/\/www.rrebase.com\/posts\/full-guide-to-testing-javascript-react#pros\"><\/a>Pros:<\/h4>\n\n\n\n<ul><li>Blazingly fast, 100s of tests can run in seconds<\/li><li>Very little setup is needed to get up and running<\/li><li>Quick feedback<\/li><li>Reliable<\/li><\/ul>\n\n\n\n<h4 id=\"cons\"><a href=\"https:\/\/www.rrebase.com\/posts\/full-guide-to-testing-javascript-react#cons\"><\/a>Cons:<\/h4>\n\n\n\n<ul><li>Too much focus on implementation details<\/li><\/ul>\n\n\n\n<h3 id=\"2-integration-tests\"><a href=\"https:\/\/www.rrebase.com\/posts\/full-guide-to-testing-javascript-react#2-integration-tests\"><\/a>2. Integration tests<\/h3>\n\n\n\n<p>Test that multiple components &amp; layers work together as expected (React Testing Library &amp; Cypress).<\/p>\n\n\n\n<p>This might mean testing components with React Testing Library that render many child components (think of it as testing a whole Form component vs testing each element of the form) or using Cypress and testing it through the browser (any API calls to the server are stubbed).<\/p>\n\n\n\n<h4 id=\"pros-1\"><a href=\"https:\/\/www.rrebase.com\/posts\/full-guide-to-testing-javascript-react#pros-1\"><\/a>Pros:<\/h4>\n\n\n\n<ul><li>Relatively fast (browser level) tests<\/li><li>Testing like a user would<\/li><li>Little effort to cover a lot of application code<\/li><\/ul>\n\n\n\n<h4 id=\"cons-1\"><a href=\"https:\/\/www.rrebase.com\/posts\/full-guide-to-testing-javascript-react#cons-1\"><\/a>Cons:<\/h4>\n\n\n\n<ul><li>Flaky in Continuous Integration (environment differences)<\/li><\/ul>\n\n\n\n<h3 id=\"3-end-to-end-e2e-functional-tests\"><a href=\"https:\/\/www.rrebase.com\/posts\/full-guide-to-testing-javascript-react#3-end-to-end-e2e-functional-tests\"><\/a>3. End-to-End (E2E) functional tests<\/h3>\n\n\n\n<p>Test all layers of your application by&nbsp;<strong>running the complete app<\/strong>. This means React, your API, the database and any extra services there may be. These tests can even run against the staging\/production server. Testing in production can bring a lot of value and it\u2019s relatively easy to set up, but it must be done in a safe manner.<\/p>\n\n\n\n<h4 id=\"pros-2\"><a href=\"https:\/\/www.rrebase.com\/posts\/full-guide-to-testing-javascript-react#pros-2\"><\/a>Pros:<\/h4>\n\n\n\n<ul><li>Gives high confidence that a critical business flow actually works<\/li><li>Most similar to how a user would test it<\/li><\/ul>\n\n\n\n<h4 id=\"cons-2\"><a href=\"https:\/\/www.rrebase.com\/posts\/full-guide-to-testing-javascript-react#cons-2\"><\/a>Cons:<\/h4>\n\n\n\n<ul><li>Initial tests require a lot of application-specific setup<\/li><li>Expensive, slow setup speed for each test<\/li><li>Extra flaky in CI (more potential environment differences)<\/li><\/ul>\n\n\n\n<h3 id=\"4-static-tests-analysis\"><a href=\"https:\/\/www.rrebase.com\/posts\/full-guide-to-testing-javascript-react#4-static-tests-analysis\"><\/a>4. Static tests (analysis)<\/h3>\n\n\n\n<p>Static analysis can also be considered a type of testing. Consider using TypeScript and tools such as Eslint &amp; Prettier. Linters not only increase overall code quality but also help catch bugs and syntax errors even before code execution.<\/p>\n\n\n\n<h4 id=\"pros-3\"><a href=\"https:\/\/www.rrebase.com\/posts\/full-guide-to-testing-javascript-react#pros-3\"><\/a>Pros:<\/h4>\n\n\n\n<ul><li>Speed up development<\/li><li>Catch bugs at compile time<\/li><li>Rich IDE support<\/li><li>Improved code quality<\/li><\/ul>\n\n\n\n<h4 id=\"cons-3\"><a href=\"https:\/\/www.rrebase.com\/posts\/full-guide-to-testing-javascript-react#cons-3\"><\/a>Cons:<\/h4>\n\n\n\n<ul><li>Time investment for initial configuration<\/li><li>TypeScript compilation can get expensive in complex projects<\/li><\/ul>\n\n\n\n<h3 id=\"-which-types-of-tests-to-use\"><a href=\"https:\/\/www.rrebase.com\/posts\/full-guide-to-testing-javascript-react#-which-types-of-tests-to-use\"><\/a>? Which types of tests to use?<\/h3>\n\n\n\n<p>In an ideal world, one would use a mix of all the testing types. It largely depends on the structure of the project. Starting with static analysis tools and unit tests is probably easiest for most simple applications. Try to set up integration tests as early as possible and eventually cover happy paths of all critical parts with E2E tests (e.g signup &amp; login flows).<\/p>\n\n\n\n<p>Focus on tests that you feel give the most value.<\/p>\n\n\n\n<h2 id=\"\u2642\ufe0f-examples\"><a href=\"https:\/\/www.rrebase.com\/posts\/full-guide-to-testing-javascript-react#%E2%99%82%EF%B8%8F-examples\"><\/a>?\u200d\u2642\ufe0f Examples<\/h2>\n\n\n\n<p>I\u2019ll show you code samples from an open-source&nbsp;<a href=\"https:\/\/github.com\/rrebase\/knboard\" target=\"_blank\" rel=\"noreferrer noopener\">Kanban App<\/a>. Links are provided to each source for any extra context if needed.<\/p>\n\n\n\n<h3 id=\"unit-tests\"><a href=\"https:\/\/www.rrebase.com\/posts\/full-guide-to-testing-javascript-react#unit-tests\"><\/a>Unit tests<\/h3>\n\n\n\n<p>A unit test for a color utility function. As simple as it gets.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>it(\"should return white as contrast for black and vice versa\", () => {\n  expect(getContrastColor(\"#FFFFFF\")).toEqual(\"#000000\");\n  expect(getContrastColor(\"#000000\")).toEqual(\"#FFFFFF\");\n});<\/code><\/pre>\n\n\n\n<p><a href=\"https:\/\/github.com\/rrebase\/knboard\/blob\/4828bac685e13108e6f35e87474876fa938c74a1\/frontend\/src\/utils\/utils.test.tsx#L41\" target=\"_blank\" rel=\"noreferrer noopener\">Source<\/a><\/p>\n\n\n\n<p>A test for a React component that uses a Popover component from Material-UI to display some text when opened. All of the state is managed inside the component so we can just click the button and assert for visible text. This is often called a&nbsp;<a href=\"https:\/\/en.wikipedia.org\/wiki\/Smoke_testing_(software)\" target=\"_blank\" rel=\"noreferrer noopener\">Smoke test<\/a>&nbsp;&#8211; reveals simple failures for very little effort.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import React from \"react\";\nimport { fireEvent, render, screen, waitFor } from \"@testing-library\/react\";\nimport Footer from \".\/Footer\";\n\nit(\"should open popover and have text visible\", () => {\n  render(&lt;Footer \/>);\n  fireEvent.click(screen.getByRole(\"button\", { name: \"About\" }));\n  expect(screen.getByRole(\"link\", { name: \"GitHub\" })).toBeVisible();\n});<\/code><\/pre>\n\n\n\n<p><a href=\"https:\/\/github.com\/rrebase\/knboard\/blob\/4828bac685e13108e6f35e87474876fa938c74a1\/frontend\/src\/features\/auth\/Footer.test.tsx#L5\" target=\"_blank\" rel=\"noreferrer noopener\">Source<\/a><\/p>\n\n\n\n<p><strong>Jest<\/strong>&nbsp;also offers Snapshot Testing. It\u2019s an awesome feature used to save a copy of the UI.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import React from \"react\";\nimport { fireEvent, render, screen, waitFor } from \"@testing-library\/react\";\nimport Footer from \".\/Footer\";\n\nit(\"should render github link correctly\", async () => {\n  render(&lt;Footer \/>);\n  fireEvent.click(screen.getByText(\"About\"));\n  await waitFor(() => {\n    expect(screen.getByRole(\"alert\")).toBeVisible();\n  });\n  expect(screen.getByRole(\"link\", { name: \"GitHub\" })).toMatchSnapshot();\n});<\/code><\/pre>\n\n\n\n<p><a href=\"https:\/\/github.com\/rrebase\/knboard\/blob\/4828bac685e13108e6f35e87474876fa938c74a1\/frontend\/src\/features\/auth\/Footer.test.tsx#L11\" target=\"_blank\" rel=\"noreferrer noopener\">Source<\/a><\/p>\n\n\n\n<p>After running the test the following snapshot file is created that should be committed alongside code changes.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>exports&#91;`should render github link correctly 1`] = `\n&lt;a\n  class=\"MuiTypography-root MuiLink-root MuiLink-underlineHover MuiTypography-colorPrimary\"\n  href=\"https:\/\/github.com\/rrebase\/knboard\"\n  rel=\"noopener\"\n  target=\"_blank\"\n>\n  GitHub\n&lt;\/a>\n`;<\/code><\/pre>\n\n\n\n<p>Next time when running the test, jest will compare the changes and fail when snapshots don\u2019t match. Similarly to a smoke test, it gives a lot of value for little effort.<\/p>\n\n\n\n<p>One ideal use case of snapshot testing is refactoring legacy codebases. You could create snapshots of components that are not tested and have the confidence of changing code and knowing when something breaks unexpectedly.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote\"><p>One other type of snapshot testing is Visual Testing, which compares the final pixels of UI instead of comparing the underlying HTML structure. So the snapshots are browser level screenshots saved as images.&nbsp;<a href=\"https:\/\/storybook.js.org\/\" target=\"_blank\" rel=\"noreferrer noopener\">Storybook<\/a>&nbsp;in combination with StoryShots is a great example of Automated Visual Testing.<\/p><\/blockquote>\n\n\n\n<p>? Now let\u2019s look at a more complex test. The reality is that most tests require a significant amount of mocking.<\/p>\n\n\n\n<p>The following test could also be categorized as an integration test as it tests&nbsp;<code>LabelDialog<\/code>, which renders components such as&nbsp;<code>LabelCreate<\/code>,&nbsp;<code>LabelRow<\/code>&nbsp;and&nbsp;<code>LabelFields<\/code>.<\/p>\n\n\n\n<p>Here we mock API calls, Redux actions, and&nbsp;<code>window.confirm<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import React from \"react\";\nimport { screen, waitFor, fireEvent } from \"@testing-library\/react\";\nimport { API_LABELS } from \"api\";\nimport {\n  rootInitialState,\n  renderWithProviders,\n  axiosMock,\n} from \"utils\/testHelpers\";\nimport { createInfoToast } from \"features\/toast\/ToastSlice\";\nimport LabelDialog from \".\/LabelDialog\";\nimport { deleteLabel } from \".\/LabelSlice\";\n\n\/\/ ...\n\nit(\"should have one label and dispatch deleteLabel\", async () => {\n  axiosMock.onDelete(`${API_LABELS}${docLabel.id}\/`).reply(204);\n  window.confirm = jest.fn().mockImplementation(() => true);\n  const { getActionsTypes } = renderWithProviders(&lt;LabelDialog \/>, {\n    ...rootInitialState,\n    board: { ...rootInitialState.board, detail: boardDetail },\n    label: {\n      ...rootInitialState.label,\n      dialogOpen: true,\n      ids: &#91;docLabel.id],\n      entities: { &#91;docLabel.id]: docLabel },\n    },\n  });\n  expect(screen.getByRole(\"heading\", { name: \"1 label\" })).toBeVisible();\n  expect(screen.getByText(docLabel.name)).toBeVisible();\n  fireEvent.click(screen.getByRole(\"button\", { name: \/delete\/i }));\n  await waitFor(() =>\n    expect(getActionsTypes().includes(deleteLabel.fulfilled.type)).toBe(true)\n  );\n  expect(getActionsTypes()).toEqual(&#91;\n    deleteLabel.pending.type,\n    createInfoToast.type,\n    deleteLabel.fulfilled.type,\n  ]);\n});<\/code><\/pre>\n\n\n\n<p><a href=\"https:\/\/github.com\/rrebase\/knboard\/blob\/4828bac685e13108e6f35e87474876fa938c74a1\/frontend\/src\/features\/label\/LabelDialog.test.tsx#L79\" target=\"_blank\" rel=\"noreferrer noopener\">Source<\/a><\/p>\n\n\n\n<p>Let\u2019s look at this line by line. So the goal of this test is to make sure that an API call would be dispatched by when a delete button is clicked for a label.<\/p>\n\n\n\n<p>Some of the state in&nbsp;<code>LabelDialog<\/code>&nbsp;is handled by Redux and not by the component (using&nbsp;<code>useState<\/code>). We\u2019re completely mocking the redux store here to test the component in isolation.<\/p>\n\n\n\n<p>Since the component has to be wrapped with a Redux&nbsp;<code>&lt;Provider \/&gt;<\/code>&nbsp;and a&nbsp;<code>&lt;Router \/&gt;<\/code>&nbsp;when testing routing, a testing helper&nbsp;<code>renderWithProviders<\/code>&nbsp;is used for rendering. It also provides access to the&nbsp;<code>mockStore<\/code>&nbsp;and&nbsp;<code>getActionsTypes<\/code>&nbsp;for common assertions.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ ...\n\nexport const renderWithProviders = (\n  ui: React.ReactNode,\n  initialState: RootState = rootInitialState\n) => {\n  const store = configureStore(&#91;thunk])(initialState);\n  return {\n    ...render(\n      &lt;MemoryRouter>\n        &lt;Provider store={store}>{ui}&lt;\/Provider>\n      &lt;\/MemoryRouter>\n    ),\n    mockStore: store,\n    getActionsTypes: () => store.getActions().map((a) => a.type),\n  };\n};<\/code><\/pre>\n\n\n\n<p><a href=\"https:\/\/github.com\/rrebase\/knboard\/blob\/4828bac685e13108e6f35e87474876fa938c74a1\/frontend\/src\/utils\/testHelpers.tsx#L35\" target=\"_blank\" rel=\"noreferrer noopener\">Source<\/a><\/p>\n\n\n\n<p>The&nbsp;<code>rootInitialState<\/code>&nbsp;is based on the initial state of all Redux reducers and is provided to the mock store and overridden to get the desired initial state for a component (one label in this case). Since&nbsp;<a href=\"https:\/\/github.com\/axios\/axios\" target=\"_blank\" rel=\"noreferrer noopener\">axios<\/a>&nbsp;is used to make API calls,&nbsp;<code>axios-mock-adapter<\/code>&nbsp;is used for easy mocking of requests.<\/p>\n\n\n\n<p>So first we mock the API delete call and then the&nbsp;<code>window.confirm<\/code>&nbsp;using a&nbsp;<a href=\"https:\/\/jestjs.io\/docs\/en\/mock-functions\" target=\"_blank\" rel=\"noreferrer noopener\">jest mock function<\/a>. Now with the proper initial redux state we have a label that we can delete. Since it\u2019s an async call, let\u2019s properly&nbsp;<code>await<\/code>&nbsp;for&nbsp;<code>deleteLabel.fulfilled<\/code>&nbsp;action to be dispatched and finally assert that the dispatched actions were the correct ones. And that\u2019s it for that test! As you can see, it\u2019s easy to get stuck in the implementation details.<\/p>\n\n\n\n<p>The Redux reducers have to be tested separately since the store was mocked. Thankfully reducers are not complex. They take a state and an action as an input and output the changed state.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>it(\"should set error on fail\", () => {\n  const errorMsg = \"Failed to fetch boards.\";\n  expect(\n    boardReducer(\n      { ...rootInitialState.board, fetchLoading: true, fetchError: null },\n      { type: fetchAllBoards.rejected, payload: errorMsg }\n    )\n  ).toEqual({\n    ...rootInitialState.board,\n    fetchLoading: false,\n    fetchError: errorMsg\n});<\/code><\/pre>\n\n\n\n<p><a href=\"https:\/\/github.com\/rrebase\/knboard\/blob\/4828bac685e13108e6f35e87474876fa938c74a1\/frontend\/src\/features\/board\/BoardList.test.tsx#L71\" target=\"_blank\" rel=\"noreferrer noopener\">Source<\/a><\/p>\n\n\n\n<h3 id=\"integration-tests\"><a href=\"https:\/\/www.rrebase.com\/posts\/full-guide-to-testing-javascript-react#integration-tests\"><\/a>Integration tests<\/h3>\n\n\n\n<p>Here\u2019s a test that edits the column title if it\u2019s not empty and also tries to cancel editing via escape key.<\/p>\n\n\n\n<p>We don\u2019t have to know anything about redux here. Yay! Just like a user. It\u2019s only an implementation detail, which is not visible at the browser level.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img src=\"https:\/\/www.rrebase.com\/media\/knboard-column-spec.png\" alt=\"Integration test\"\/><\/figure>\n\n\n\n<p>Note that it\u2019s usually best practice to test each small thing separately, but that\u2019s not a good idea for integration and E2E tests, which use the browser where each test setup &amp; teardown is slow. So it\u2019s better to test all related actions in one go.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>it(\"should edit column title if not empty &amp; cancel via esc\", () => {\n  cy.fixture(\"internals_board\").then((board) => {\n    const colTitle = \"In progress\";\n    const newColTitle = \"Ongoing\";\n    const newColumn = board.columns.find((c) => c.id === 3);\n    cy.route(\"PATCH\", \"\/api\/columns\/3\/\", {\n      ...newColumn,\n      title: newColTitle,\n    });\n\n    cy.findAllByText(newColTitle).should(\"not.exist\");\n    cy.findByText(colTitle).should(\"be.visible\");\n\n    cy.findByTestId(`col-${colTitle}`).within(() => {\n      cy.findAllByTestId(\"column-title-textarea\").should(\"not.exist\");\n      cy.findByTestId(\"column-title\").click();\n\n      cy.findByTestId(\"column-title-textarea\")\n        .clear()\n        .type(\"{enter}\")\n        .should(\"be.visible\");\n\n      cy.findByTestId(\"column-title-textarea\").type(`${newColTitle}{enter}`);\n      cy.findByTestId(\"column-title\").should(\"be.visible\");\n    });\n    cy.findAllByText(colTitle).should(\"not.exist\");\n    cy.findByText(newColTitle).should(\"be.visible\");\n\n    cy.findByTestId(`col-${newColTitle}`).within(() => {\n      cy.findByTestId(\"column-title\").click();\n      cy.findByTestId(\"column-title-textarea\").type(\"Cancelled title{esc}\");\n      cy.findAllByTestId(\"column-title-textarea\").should(\"not.exist\");\n    });\n    cy.findByText(newColTitle).should(\"be.visible\");\n  });\n});<\/code><\/pre>\n\n\n\n<p><a href=\"https:\/\/github.com\/rrebase\/knboard\/blob\/4828bac685e13108e6f35e87474876fa938c74a1\/frontend\/cypress\/integration\/stubbed\/column.spec.js#L12\" target=\"_blank\" rel=\"noreferrer noopener\">GitHub Source<\/a><\/p>\n\n\n\n<h3 id=\"e2e-functional-tests\"><a href=\"https:\/\/www.rrebase.com\/posts\/full-guide-to-testing-javascript-react#e2e-functional-tests\"><\/a>E2E functional tests<\/h3>\n\n\n\n<p>Here\u2019s a simple example of safely testing the Login functionality in Production. It\u2019s arguably one of the most critical application flows and it\u2019s really simple to test with Cypress. We just need to have a test user created beforehand.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>context(\"Production Auth\", () => {\n  beforeEach(() => {\n    cy.visit(\"https:\/\/knboard.com\/\");\n  });\n\n  it(\"should login successfully\", () => {\n    cy.findByRole(\"button\", { name: \"Login\" }).click();\n    cy.findByLabelText(\"Username\").type(\"e2etestuser\");\n    cy.findByLabelText(\"Password\").type(\"testpassword123{enter}\");\n    cy.findByRole(\"button\", { name: \"Login\" }).click();\n    cy.findByRole(\"button\", { name: \"View Boards\" }).should(\"be.visible\");\n  });\n});<\/code><\/pre>\n\n\n\n<p>It\u2019s a good idea to run a small set of smoke tests like this against production. To run this test against the local development server as most of your tests should, point Cypress to visit localhost instead of the production URL.<\/p>\n\n\n\n<p>Of course Cypress is only one of the possible tools for E2E testing. It can be done with any browser automation framework:&nbsp;<a href=\"https:\/\/github.com\/puppeteer\/puppeteer\" target=\"_blank\" rel=\"noreferrer noopener\">Puppeteer<\/a>,&nbsp;<a href=\"https:\/\/robotframework.org\/\" target=\"_blank\" rel=\"noreferrer noopener\">Robot Framework<\/a>.<\/p>\n\n\n\n<h2 id=\"conclusion\"><a href=\"https:\/\/www.rrebase.com\/posts\/full-guide-to-testing-javascript-react#conclusion\"><\/a>Conclusion<\/h2>\n\n\n\n<p>Add confidence to your codebase by using a mix of different types of tests. Start with static analysis tools and smoke tests for a lot of value with little effort. Then add unit tests, setup integration tests &amp; a couple of E2E tests for the business-critical flows.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Want to have more confidence in your codebase when making changes? Let\u2019s look at the modern ways and tools used to test JavaScript, TypeScript &amp; React.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[30],"tags":[95,65,114],"_links":{"self":[{"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=\/wp\/v2\/posts\/1415"}],"collection":[{"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1415"}],"version-history":[{"count":1,"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=\/wp\/v2\/posts\/1415\/revisions"}],"predecessor-version":[{"id":1416,"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=\/wp\/v2\/posts\/1415\/revisions\/1416"}],"wp:attachment":[{"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1415"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1415"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1415"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}