From fa172f9343976945e63303a58bbe33dad603908e Mon Sep 17 00:00:00 2001 From: Lior Keren Date: Wed, 26 Oct 2022 18:28:36 +0300 Subject: [PATCH 01/10] Added Hidden Files Should be amended to the initial commit of Ruby On Rails --- .browserslistrc | 1 + .gitattributes | 10 ++++++++++ .gitignore | 2 +- .ruby-version | 1 + 4 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 .browserslistrc create mode 100644 .gitattributes create mode 100644 .ruby-version diff --git a/.browserslistrc b/.browserslistrc new file mode 100644 index 0000000..e94f814 --- /dev/null +++ b/.browserslistrc @@ -0,0 +1 @@ +defaults diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..5168571 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,10 @@ +# See https://git-scm.com/docs/gitattributes for more about git attribute files. + +# Mark the database schema as having been generated. +db/schema.rb linguist-generated + +# Mark the yarn lockfile as having been generated. +yarn.lock linguist-generated + +# Mark any vendored files as having been vendored. +vendor/* linguist-vendored diff --git a/.gitignore b/.gitignore index 6eac730..7efd766 100644 --- a/.gitignore +++ b/.gitignore @@ -34,7 +34,6 @@ /public/packs /public/packs-test -/node_modules /yarn-error.log yarn-debug.log* .yarn-integrity @@ -43,3 +42,4 @@ yarn-debug.log* Gemfile.lock package-lock.json yarn.lock +node_modules/ diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..4560fb9 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +ruby-2.6.3 From ebcb2988f71c6522999120e981e7de77ccaffa3c Mon Sep 17 00:00:00 2001 From: Lior Keren Date: Wed, 2 Nov 2022 15:35:46 +0200 Subject: [PATCH 02/10] Added Guitar Gallery This Component presents the guitars gotten from the server. It contains a sub-component: GuitarInfo which is a GalleryItem In addition this commit removes the unecessary files regarding Users.js --- .ruby-version | 1 - app/controllers/guitars_controller.rb | 24 +++++++++++ app/javascript/components/GuitarGallery.js | 45 +++++++++++++++++++++ app/javascript/components/GuitarInfo.js | 38 +++++++++++++++++ app/javascript/components/Users.js | 31 -------------- app/javascript/packs/hello_react.jsx | 4 +- app/views/home/index.html.erb | 1 - app/views/user/index.html.erb | 2 - config/routes.rb | 5 +-- test/controllers/guitars_controller_test.rb | 10 +++++ test/controllers/user_controller_test.rb | 10 ----- 11 files changed, 121 insertions(+), 50 deletions(-) delete mode 100644 .ruby-version create mode 100644 app/controllers/guitars_controller.rb create mode 100644 app/javascript/components/GuitarGallery.js create mode 100644 app/javascript/components/GuitarInfo.js delete mode 100644 app/javascript/components/Users.js delete mode 100644 app/views/user/index.html.erb create mode 100644 test/controllers/guitars_controller_test.rb delete mode 100644 test/controllers/user_controller_test.rb diff --git a/.ruby-version b/.ruby-version deleted file mode 100644 index 4560fb9..0000000 --- a/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -ruby-2.6.3 diff --git a/app/controllers/guitars_controller.rb b/app/controllers/guitars_controller.rb new file mode 100644 index 0000000..47d09bf --- /dev/null +++ b/app/controllers/guitars_controller.rb @@ -0,0 +1,24 @@ +class GuitarsController < ApplicationController + def index + render :json => [ + { + 'name':'guitar1', + 'url':'https://rukminim1.flixcart.com/image/416/416/acoustic-guitar/x/8/w/topaz-blue-signature-original-imaefec7uhypjdr9.jpeg?q=70', + 'price':'100', + 'description':'blablabla 1' + }, + { + 'name':'guitar2', + 'url':'https://shop.brianmayguitars.co.uk/user/special/content/Antique%20Cherry%20a.jpg', + 'price':'200', + 'description':'blablabla 2' + }, + { + 'name':'guitar3', + 'url':'https://cdn.mos.cms.futurecdn.net/Yh6r74b8CAj2jbdf2FAhq4-970-80.jpg.webp', + 'price':'300', + 'description':'blablabla 3' + } + ] + end +end diff --git a/app/javascript/components/GuitarGallery.js b/app/javascript/components/GuitarGallery.js new file mode 100644 index 0000000..c7677d6 --- /dev/null +++ b/app/javascript/components/GuitarGallery.js @@ -0,0 +1,45 @@ +import React, { useEffect, useState } from 'react'; +import axios from 'axios'; +import { Title, TitleSizes, Gallery } from '@patternfly/react-core'; +import GuitarInfo from './GuitarInfo'; + +// Loading the data from the server: + +export const GuitarGallery = () => { + const getGuitars = async () => { + try { + const response = await axios.get('/guitars'); + if (response.data) { + setGuitars(response.data); + } + } catch (error) { + console.error(error); + } + }; + + const [guitars, setGuitars] = useState([]); + + useEffect(() => { + getGuitars(); + }, []); + + return ( +
+ + Guitars For Sale: + + + {guitars.map(({ name, url, price, description }) => ( + + ))} + +
+ ); +}; diff --git a/app/javascript/components/GuitarInfo.js b/app/javascript/components/GuitarInfo.js new file mode 100644 index 0000000..3706d64 --- /dev/null +++ b/app/javascript/components/GuitarInfo.js @@ -0,0 +1,38 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { + GalleryItem, + Grid, + GridItem, + Card, + CardTitle, + CardBody, + CardFooter +} from '@patternfly/react-core'; + +const GuitarInfo = ({ name, url, price, description }) => { + return ( + + + + + Guitar Image + + + {name} + Price: {price} + Description: {description} + + + + + ); +}; +GuitarInfo.propTypes = { + name: PropTypes.string.isRequired, + url: PropTypes.string.isRequired, + price: PropTypes.string.isRequired, + description: PropTypes.string.isRequired +}; + +export default GuitarInfo; diff --git a/app/javascript/components/Users.js b/app/javascript/components/Users.js deleted file mode 100644 index e267a4c..0000000 --- a/app/javascript/components/Users.js +++ /dev/null @@ -1,31 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import axios from 'axios'; -import { Title, TitleSizes, List, ListItem } from '@patternfly/react-core'; - -export const Users = () => { - const [users, setUsers] = useState([]); - useEffect(() => { - const getUser = async () => { - try { - const response = await axios.get('/user'); - setUsers(response.data || []); - } catch (error) { - console.error(error); - } - }; - getUser(); - }, []); - - return ( -
- - Users: - - - {users.map((user, index) => ( - {user} - ))} - -
- ); -}; diff --git a/app/javascript/packs/hello_react.jsx b/app/javascript/packs/hello_react.jsx index 35a0215..5c7d640 100644 --- a/app/javascript/packs/hello_react.jsx +++ b/app/javascript/packs/hello_react.jsx @@ -3,9 +3,9 @@ // of the page. import React from 'react'; import ReactDOM from 'react-dom'; -import { Users } from '../components/Users'; +import { GuitarGallery } from '../components/GuitarGallery'; import './fonts.css'; document.addEventListener('DOMContentLoaded', () => { - ReactDOM.render(, document.body.appendChild(document.createElement('div'))); + ReactDOM.render(, document.body.appendChild(document.createElement('div'))); }); diff --git a/app/views/home/index.html.erb b/app/views/home/index.html.erb index 8b13789..e69de29 100644 --- a/app/views/home/index.html.erb +++ b/app/views/home/index.html.erb @@ -1 +0,0 @@ - diff --git a/app/views/user/index.html.erb b/app/views/user/index.html.erb deleted file mode 100644 index c825aaf..0000000 --- a/app/views/user/index.html.erb +++ /dev/null @@ -1,2 +0,0 @@ -

User#index

-

Find me in app/views/user/index.html.erb

diff --git a/config/routes.rb b/config/routes.rb index 00a4a81..da964ae 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,8 +1,7 @@ # frozen_string_literal: true Rails.application.routes.draw do - root - 'guitars#index' - + root 'home#index' + get '/guitars', to: 'guitars#index' # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html end diff --git a/test/controllers/guitars_controller_test.rb b/test/controllers/guitars_controller_test.rb new file mode 100644 index 0000000..d31d768 --- /dev/null +++ b/test/controllers/guitars_controller_test.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require 'test_helper' + +class GuitarsControllerTest < ActionDispatch::IntegrationTest + test "should get index" do + get guitars_index_url + assert_response :success + end +end diff --git a/test/controllers/user_controller_test.rb b/test/controllers/user_controller_test.rb deleted file mode 100644 index 1c6182f..0000000 --- a/test/controllers/user_controller_test.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -require 'test_helper' - -class UserControllerTest < ActionDispatch::IntegrationTest - test 'should get index' do - get user_index_url - assert_response :success - end -end From baa23fcd3f00c0880499616b4cb67698fece414f Mon Sep 17 00:00:00 2001 From: Lior Keren Date: Wed, 16 Nov 2022 18:40:58 +0200 Subject: [PATCH 03/10] Undo this commit, it should still be worked on This commit consists several commits: 1. added the const URL file which is used in GuitarGallery 2. Added a Loader to be presented while the component is still being rendered 3. Added tests for GuitarGallery --- app/__mocks__/axios.js | 18 ++ app/controllers/guitars_controller.rb | 40 ++-- app/javascript/components/GuitarGallery.js | 17 +- app/javascript/constUrls.js | 1 + jest.config.js | 205 +++++++++++++++++++- test/controllers/guitars_controller_test.rb | 2 +- test/javascript/GuitarGallery.test.js | 52 +++++ 7 files changed, 310 insertions(+), 25 deletions(-) create mode 100644 app/__mocks__/axios.js create mode 100644 app/javascript/constUrls.js create mode 100644 test/javascript/GuitarGallery.test.js diff --git a/app/__mocks__/axios.js b/app/__mocks__/axios.js new file mode 100644 index 0000000..acc7247 --- /dev/null +++ b/app/__mocks__/axios.js @@ -0,0 +1,18 @@ +export default { + get: jest.fn().mockResolvedValue({ + data: [ + { + name: 'guitar1_test', + url: 'https://rukminim1.flixcart.com/image/416/416/acoustic-guitar/x/8/w/topaz-blue-signature-original-imaefec7uhypjdr9.jpeg?q=70', + price: '100', + description: 'some description in guitar1_test' + }, + { + name: 'guitar2_test', + url: 'https://shop.brianmayguitars.co.uk/user/special/content/Antique%20Cherry%20a.jpg', + price: '200', + description: 'some description in guitar2_test' + } + ] + }) +}; diff --git a/app/controllers/guitars_controller.rb b/app/controllers/guitars_controller.rb index 47d09bf..3eb1cc4 100644 --- a/app/controllers/guitars_controller.rb +++ b/app/controllers/guitars_controller.rb @@ -1,24 +1,26 @@ +# frozen_string_literal: true + class GuitarsController < ApplicationController def index - render :json => [ - { - 'name':'guitar1', - 'url':'https://rukminim1.flixcart.com/image/416/416/acoustic-guitar/x/8/w/topaz-blue-signature-original-imaefec7uhypjdr9.jpeg?q=70', - 'price':'100', - 'description':'blablabla 1' - }, - { - 'name':'guitar2', - 'url':'https://shop.brianmayguitars.co.uk/user/special/content/Antique%20Cherry%20a.jpg', - 'price':'200', - 'description':'blablabla 2' - }, - { - 'name':'guitar3', - 'url':'https://cdn.mos.cms.futurecdn.net/Yh6r74b8CAj2jbdf2FAhq4-970-80.jpg.webp', - 'price':'300', - 'description':'blablabla 3' - } + render json: [ + { + name: 'guitar1', + url: 'https://rukminim1.flixcart.com/image/416/416/acoustic-guitar/x/8/w/topaz-blue-signature-original-imaefec7uhypjdr9.jpeg?q=70', + price: '100', + description: 'blablabla 1' + }, + { + name: 'guitar2', + url: 'https://shop.brianmayguitars.co.uk/user/special/content/Antique%20Cherry%20a.jpg', + price: '200', + description: 'blablabla 2' + }, + { + name: 'guitar3', + url: 'https://cdn.mos.cms.futurecdn.net/Yh6r74b8CAj2jbdf2FAhq4-970-80.jpg.webp', + price: '300', + description: 'blablabla 3' + } ] end end diff --git a/app/javascript/components/GuitarGallery.js b/app/javascript/components/GuitarGallery.js index c7677d6..9f23777 100644 --- a/app/javascript/components/GuitarGallery.js +++ b/app/javascript/components/GuitarGallery.js @@ -2,13 +2,18 @@ import React, { useEffect, useState } from 'react'; import axios from 'axios'; import { Title, TitleSizes, Gallery } from '@patternfly/react-core'; import GuitarInfo from './GuitarInfo'; +import guitarsUrl from '../constUrls'; +import styled from 'styled-components'; + +// is used in order to present a Loading 'screen' to the user until the data is rendered: +const Loader = styled.div``; // Loading the data from the server: export const GuitarGallery = () => { const getGuitars = async () => { try { - const response = await axios.get('/guitars'); + const response = await axios.get(guitarsUrl); if (response.data) { setGuitars(response.data); } @@ -36,9 +41,13 @@ export const GuitarGallery = () => { xl: '200px', '2xl': '300px' }}> - {guitars.map(({ name, url, price, description }) => ( - - ))} + {guitars.length === 0 ? ( + Loading... + ) : ( + guitars.map(({ name, url, price, description }) => ( + + )) + )} ); diff --git a/app/javascript/constUrls.js b/app/javascript/constUrls.js new file mode 100644 index 0000000..78c2ecb --- /dev/null +++ b/app/javascript/constUrls.js @@ -0,0 +1 @@ +export const guitarsUrl = '/guitars'; diff --git a/jest.config.js b/jest.config.js index c7d3e00..1860bb7 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,5 +1,208 @@ +/* + * For a detailed explanation regarding each configuration property, visit: + * https://jestjs.io/docs/configuration + */ // eslint-disable-next-line no-undef module.exports = { setupFilesAfterEnv: ['./rtl.setup.js'], - testEnvironment: 'jsdom' + + moduleNameMapper: { + '^.+\\.(css|scss)$': 'identity-obj-proxy' + }, + // All imported modules in your tests should be mocked automatically + // automock: false, + + // Stop running tests after `n` failures + // bail: 0, + + // The directory where Jest should store its cached dependency information + // cacheDirectory: "/tmp/jest_rs", + + // Automatically clear mock calls, instances, contexts and results before every test + clearMocks: true, + + // Indicates whether the coverage information should be collected while executing the test + // collectCoverage: false, + + // An array of glob patterns indicating a set of files for which coverage information should be collected + // collectCoverageFrom: undefined, + + // The directory where Jest should output its coverage files + // coverageDirectory: undefined, + + // An array of regexp pattern strings used to skip coverage collection + // coveragePathIgnorePatterns: [ + // "/node_modules/" + // ], + + // Indicates which provider should be used to instrument code for coverage + // coverageProvider: "babel", + + // A list of reporter names that Jest uses when writing coverage reports + // coverageReporters: [ + // "json", + // "text", + // "lcov", + // "clover" + // ], + + // An object that configures minimum threshold enforcement for coverage results + // coverageThreshold: undefined, + + // A path to a custom dependency extractor + // dependencyExtractor: undefined, + + // Make calling deprecated APIs throw helpful error messages + // errorOnDeprecated: false, + + // The default configuration for fake timers + // fakeTimers: { + // "enableGlobally": false + // }, + + // Force coverage collection from ignored files using an array of glob patterns + // forceCoverageMatch: [], + + // A path to a module which exports an async function that is triggered once before all test suites + // globalSetup: undefined, + + // A path to a module which exports an async function that is triggered once after all test suites + // globalTeardown: undefined, + + // A set of global variables that need to be available in all test environments + // globals: {}, + + // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. + // maxWorkers: "50%", + + // An array of directory names to be searched recursively up from the requiring module's location + // moduleDirectories: [ + // "node_modules" + // ], + + // An array of file extensions your modules use + // moduleFileExtensions: [ + // "js", + // "mjs", + // "cjs", + // "jsx", + // "ts", + // "tsx", + // "json", + // "node" + // ], + + // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module + // moduleNameMapper: {}, + + // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader + // modulePathIgnorePatterns: [], + + // Activates notifications for test results + // notify: false, + + // An enum that specifies notification mode. Requires { notify: true } + // notifyMode: "failure-change", + + // A preset that is used as a base for Jest's configuration + // preset: undefined, + + // Run tests from one or more projects + // projects: undefined, + + // Use this configuration option to add custom reporters to Jest + // reporters: undefined, + + // Automatically reset mock state before every test + // resetMocks: false, + + // Reset the module registry before running each individual test + // resetModules: false, + + // A path to a custom resolver + // resolver: undefined, + + // Automatically restore mock state and implementation before every test + // restoreMocks: false, + + // The root directory that Jest should scan for tests and modules within + // rootDir: undefined, + + // A list of paths to directories that Jest should use to search for files in + // roots: [ + // "" + // ], + + // Allows you to use a custom runner instead of Jest's default test runner + // runner: "jest-runner", + + // The paths to modules that run some code to configure or set up the testing environment before each test + // setupFiles: [], + + // A list of paths to modules that run some code to configure or set up the testing framework before each test + // setupFilesAfterEnv: [], + + // The number of seconds after which a test is considered as slow and reported as such in the results. + // slowTestThreshold: 5, + + // A list of paths to snapshot serializer modules Jest should use for snapshot testing + // snapshotSerializers: [], + + // The test environment that will be used for testing + testEnvironment: 'jsdom', + + // Options that will be passed to the testEnvironment + // testEnvironmentOptions: {}, + + // Adds a location field to test results + // testLocationInResults: false, + + // The glob patterns Jest uses to detect test files + // testMatch: [ + // "**/__tests__/**/*.[jt]s?(x)", + // "**/?(*.)+(spec|test).[tj]s?(x)" + // ], + + // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped + // testPathIgnorePatterns: [ + // "/node_modules/" + // ], + + // The regexp pattern or array of patterns that Jest uses to detect test files + // testRegex: [], + + // This option allows the use of a custom results processor + // testResultsProcessor: undefined, + + // This option allows use of a custom test runner + // testRunner: "jest-circus/runner", + + // A map from regular expressions to paths to transformers + // transform: undefined, + + // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation + // transformIgnorePatterns: ['/node_modules/', '\\.pnp\\.[^\\/]+$'] + + transformIgnorePatterns: [ + 'node_modules/(?!(react-native' + + '|react-navigation-tabs' + + '|react-native-splash-screen' + + '|react-native-screens' + + '|react-native-reanimated' + + ')/)', + 'node_modules/(?!(jest-)?react-native|react-(native|universal|navigation)-(.*)|@react-native-community/(.*)|@react-navigation/(.*)|bs-platform|(@[a-zA-Z]+/)?(bs|reason|rescript)-(.*)+)', + '/node_modules/' + ] + + // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them + // unmockedModulePathPatterns: undefined, + + // Indicates whether each individual test should be reported during the run + // verbose: undefined, + + // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode + // watchPathIgnorePatterns: [], + + // Whether to use watchman for file crawling + // watchman: true, }; diff --git a/test/controllers/guitars_controller_test.rb b/test/controllers/guitars_controller_test.rb index d31d768..7810ff6 100644 --- a/test/controllers/guitars_controller_test.rb +++ b/test/controllers/guitars_controller_test.rb @@ -3,7 +3,7 @@ require 'test_helper' class GuitarsControllerTest < ActionDispatch::IntegrationTest - test "should get index" do + test 'get index' do get guitars_index_url assert_response :success end diff --git a/test/javascript/GuitarGallery.test.js b/test/javascript/GuitarGallery.test.js new file mode 100644 index 0000000..485a750 --- /dev/null +++ b/test/javascript/GuitarGallery.test.js @@ -0,0 +1,52 @@ +import React from 'react'; +import { render, screen /*, waitForElement*/ } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; +import axios from 'axios'; +import GuitarGallery from '../../app/javascript/components/GuitarGallery'; +import guitarsUrl from '../../javascript/constUrls'; + +test("show loader when it's fetching data, then render Guitars", async () => { + axios.get.mockResolvedValueOnce({ + data: [ + { + name: 'guitar1_test', + url: 'https://rukminim1.flixcart.com/image/416/416/acoustic-guitar/x/8/w/topaz-blue-signature-original-imaefec7uhypjdr9.jpeg?q=70', + price: '100', + description: 'some description in guitar1_test' + }, + { + name: 'guitar2_test', + url: 'https://shop.brianmayguitars.co.uk/user/special/content/Antique%20Cherry%20a.jpg', + price: '200', + description: 'some description in guitar2_test' + } + ] + }); + + //show loader + const { unmount, /*getAllByTestId,*/ getByText } = render(); + expect(getByText(/loading.../i)).toBeInTheDocument(); + + // check the correct url is called: + expect(axios.get).toHaveBeenCalledWith(guitarsUrl); + expect(axios.get).toHaveBeenCalledTimes(1); + + // //check what's rendered in the row + // const rowValues = await waitForElement(() => getAllByTestId('row').map((row) => row.textContent)); + // expect(rowValues).toEqual(['ali', 'abu']); + + const guitarDescriptions = screen.findAllBy('Description'); + const expectedDescription = [ + 'some description in guitar1_test', + 'some description in guitar2_test' + ]; + // option 1: + expect(guitarDescriptions).toEqual(expectedDescription); + // option 2: + guitarDescriptions.forEach((description, index) => { + expect(description).toBe(expectedDescription.at(index)); + }); + + // unmnount the component from the DOM + unmount(); +}); From f863854e47a8f421605fb80f39dc9c7222af6f9e Mon Sep 17 00:00:00 2001 From: Lior Keren Date: Thu, 17 Nov 2022 13:17:19 +0200 Subject: [PATCH 04/10] this solved the import problem --- app/javascript/components/GuitarGallery.js | 14 ++++++-------- .../javascript/components}/GuitarGallery.test.js | 16 +++++++++------- .../{constUrls.js => components/constants.js} | 0 app/javascript/packs/hello_react.jsx | 2 +- package.json | 1 + 5 files changed, 17 insertions(+), 16 deletions(-) rename {test/javascript => app/javascript/components}/GuitarGallery.test.js (76%) rename app/javascript/{constUrls.js => components/constants.js} (100%) diff --git a/app/javascript/components/GuitarGallery.js b/app/javascript/components/GuitarGallery.js index 9f23777..4a51896 100644 --- a/app/javascript/components/GuitarGallery.js +++ b/app/javascript/components/GuitarGallery.js @@ -1,16 +1,12 @@ import React, { useEffect, useState } from 'react'; import axios from 'axios'; -import { Title, TitleSizes, Gallery } from '@patternfly/react-core'; +import { Title, TitleSizes, Gallery, Spinner } from '@patternfly/react-core'; import GuitarInfo from './GuitarInfo'; -import guitarsUrl from '../constUrls'; -import styled from 'styled-components'; - -// is used in order to present a Loading 'screen' to the user until the data is rendered: -const Loader = styled.div``; +import { guitarsUrl } from './constants'; // Loading the data from the server: -export const GuitarGallery = () => { +const GuitarGallery = () => { const getGuitars = async () => { try { const response = await axios.get(guitarsUrl); @@ -42,7 +38,7 @@ export const GuitarGallery = () => { '2xl': '300px' }}> {guitars.length === 0 ? ( - Loading... + ) : ( guitars.map(({ name, url, price, description }) => ( @@ -52,3 +48,5 @@ export const GuitarGallery = () => { ); }; + +export default GuitarGallery; diff --git a/test/javascript/GuitarGallery.test.js b/app/javascript/components/GuitarGallery.test.js similarity index 76% rename from test/javascript/GuitarGallery.test.js rename to app/javascript/components/GuitarGallery.test.js index 485a750..d3cd751 100644 --- a/test/javascript/GuitarGallery.test.js +++ b/app/javascript/components/GuitarGallery.test.js @@ -2,8 +2,8 @@ import React from 'react'; import { render, screen /*, waitForElement*/ } from '@testing-library/react'; import '@testing-library/jest-dom/extend-expect'; import axios from 'axios'; -import GuitarGallery from '../../app/javascript/components/GuitarGallery'; -import guitarsUrl from '../../javascript/constUrls'; +import GuitarGallery from './GuitarGallery'; +import { guitarsUrl } from './constants'; test("show loader when it's fetching data, then render Guitars", async () => { axios.get.mockResolvedValueOnce({ @@ -25,7 +25,7 @@ test("show loader when it's fetching data, then render Guitars", async () => { //show loader const { unmount, /*getAllByTestId,*/ getByText } = render(); - expect(getByText(/loading.../i)).toBeInTheDocument(); + // expect(getByText(/loading.../i)).toBeInTheDocument(); // check the correct url is called: expect(axios.get).toHaveBeenCalledWith(guitarsUrl); @@ -35,7 +35,8 @@ test("show loader when it's fetching data, then render Guitars", async () => { // const rowValues = await waitForElement(() => getAllByTestId('row').map((row) => row.textContent)); // expect(rowValues).toEqual(['ali', 'abu']); - const guitarDescriptions = screen.findAllBy('Description'); + const guitarDescriptions = await screen.findAllByText(/Description/i); + // const guitarDescriptions = screen.findAllBy('Description'); const expectedDescription = [ 'some description in guitar1_test', 'some description in guitar2_test' @@ -43,9 +44,10 @@ test("show loader when it's fetching data, then render Guitars", async () => { // option 1: expect(guitarDescriptions).toEqual(expectedDescription); // option 2: - guitarDescriptions.forEach((description, index) => { - expect(description).toBe(expectedDescription.at(index)); - }); + // console.log(guitarDescriptions); + // guitarDescriptions.forEach((description, index) => { + // expect(description).toBe(expectedDescription.at(index)); + // }); // unmnount the component from the DOM unmount(); diff --git a/app/javascript/constUrls.js b/app/javascript/components/constants.js similarity index 100% rename from app/javascript/constUrls.js rename to app/javascript/components/constants.js diff --git a/app/javascript/packs/hello_react.jsx b/app/javascript/packs/hello_react.jsx index 5c7d640..50ef0d4 100644 --- a/app/javascript/packs/hello_react.jsx +++ b/app/javascript/packs/hello_react.jsx @@ -3,7 +3,7 @@ // of the page. import React from 'react'; import ReactDOM from 'react-dom'; -import { GuitarGallery } from '../components/GuitarGallery'; +import GuitarGallery from '../components/GuitarGallery'; import './fonts.css'; document.addEventListener('DOMContentLoaded', () => { diff --git a/package.json b/package.json index b5e0d73..d9d6e61 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-react": "^7.31.10", "global-jsdom": "^8.6.0", + "identity-obj-proxy": "^3.0.0", "jest": "^29.3.0", "jest-environment-jsdom": "^29.3.1", "jsdom": "^20.0.2", From 7c1d677281ba5402d9bb9908e8557c3457f35159 Mon Sep 17 00:00:00 2001 From: Lior Keren Date: Tue, 22 Nov 2022 15:47:30 +0200 Subject: [PATCH 05/10] Exporting fixutes of const data used in the tests of GuitarGallery Exporting fixutes of const guitars data used in the tests of the GuitarGallery to an external fixtures file --- .../components/GuitarGallery.test.js | 54 ------------------- .../components/tests/GuitarGallery.test.js | 46 ++++++++++++++++ app/javascript/components/tests/fixtures.js | 16 ++++++ 3 files changed, 62 insertions(+), 54 deletions(-) delete mode 100644 app/javascript/components/GuitarGallery.test.js create mode 100644 app/javascript/components/tests/GuitarGallery.test.js create mode 100644 app/javascript/components/tests/fixtures.js diff --git a/app/javascript/components/GuitarGallery.test.js b/app/javascript/components/GuitarGallery.test.js deleted file mode 100644 index d3cd751..0000000 --- a/app/javascript/components/GuitarGallery.test.js +++ /dev/null @@ -1,54 +0,0 @@ -import React from 'react'; -import { render, screen /*, waitForElement*/ } from '@testing-library/react'; -import '@testing-library/jest-dom/extend-expect'; -import axios from 'axios'; -import GuitarGallery from './GuitarGallery'; -import { guitarsUrl } from './constants'; - -test("show loader when it's fetching data, then render Guitars", async () => { - axios.get.mockResolvedValueOnce({ - data: [ - { - name: 'guitar1_test', - url: 'https://rukminim1.flixcart.com/image/416/416/acoustic-guitar/x/8/w/topaz-blue-signature-original-imaefec7uhypjdr9.jpeg?q=70', - price: '100', - description: 'some description in guitar1_test' - }, - { - name: 'guitar2_test', - url: 'https://shop.brianmayguitars.co.uk/user/special/content/Antique%20Cherry%20a.jpg', - price: '200', - description: 'some description in guitar2_test' - } - ] - }); - - //show loader - const { unmount, /*getAllByTestId,*/ getByText } = render(); - // expect(getByText(/loading.../i)).toBeInTheDocument(); - - // check the correct url is called: - expect(axios.get).toHaveBeenCalledWith(guitarsUrl); - expect(axios.get).toHaveBeenCalledTimes(1); - - // //check what's rendered in the row - // const rowValues = await waitForElement(() => getAllByTestId('row').map((row) => row.textContent)); - // expect(rowValues).toEqual(['ali', 'abu']); - - const guitarDescriptions = await screen.findAllByText(/Description/i); - // const guitarDescriptions = screen.findAllBy('Description'); - const expectedDescription = [ - 'some description in guitar1_test', - 'some description in guitar2_test' - ]; - // option 1: - expect(guitarDescriptions).toEqual(expectedDescription); - // option 2: - // console.log(guitarDescriptions); - // guitarDescriptions.forEach((description, index) => { - // expect(description).toBe(expectedDescription.at(index)); - // }); - - // unmnount the component from the DOM - unmount(); -}); diff --git a/app/javascript/components/tests/GuitarGallery.test.js b/app/javascript/components/tests/GuitarGallery.test.js new file mode 100644 index 0000000..5bc33ec --- /dev/null +++ b/app/javascript/components/tests/GuitarGallery.test.js @@ -0,0 +1,46 @@ +import React from 'react'; +import { render, screen /*, waitForElement*/ } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; +import axios from 'axios'; +import GuitarGallery from '../GuitarGallery.js'; +import { guitarsUrl } from '../constants'; +import { guitarData } from './fixtures'; + +test("show loader when it's fetching data, then render Guitars", async () => { + axios.get.mockResolvedValueOnce(guitarData); + //show loader + const { unmount /*, getAllByTestId, getByText*/ } = render(); + // expect(getByText(/loading.../i)).toBeInTheDocument(); + + // check the correct url is called: + expect(axios.get).toHaveBeenCalledWith(guitarsUrl); + expect(axios.get).toHaveBeenCalledTimes(1); + + // //check what's rendered in the row + // const rowValues = await waitForElement(() => getAllByTestId('row').map((row) => row.textContent)); + // expect(rowValues).toEqual(['ali', 'abu']); + + // const guitarDescriptions = await screen.findAllByText(/Description/i); + // // const guitarDescriptions = screen.findAllBy('Description'); + // const expectedDescription = [ + // 'some description in guitar1_test', + // 'some description in guitar2_test' + // ]; + // // option 1: + // expect(guitarDescriptions).toEqual(expectedDescription); + // option 2: + // console.log(guitarDescriptions); + // guitarDescriptions.forEach((description, index) => { + // expect(description).toBe(expectedDescription.at(index)); + // }); + + // ensure the title of the page is presented in the page: + expect(screen.getByText('Guitars For Sale:')).toBeInTheDocument(); + + // ensure some content of the page is presented in the page: + expect(screen.getByText('some description in guitar1_test')).toBeInTheDocument(); + expect(screen.getByText('some description in guitar2_test')).toBeInTheDocument(); + + // unmnount the component from the DOM + unmount(); +}); diff --git a/app/javascript/components/tests/fixtures.js b/app/javascript/components/tests/fixtures.js new file mode 100644 index 0000000..ab8ab8d --- /dev/null +++ b/app/javascript/components/tests/fixtures.js @@ -0,0 +1,16 @@ +export const guitarData = { + data: [ + { + name: 'guitar1_test', + url: 'https://rukminim1.flixcart.com/image/416/416/acoustic-guitar/x/8/w/topaz-blue-signature-original-imaefec7uhypjdr9.jpeg?q=70', + price: '100', + description: 'some description in guitar1_test' + }, + { + name: 'guitar2_test', + url: 'https://shop.brianmayguitars.co.uk/user/special/content/Antique%20Cherry%20a.jpg', + price: '200', + description: 'some description in guitar2_test' + } + ] +}; From 060cc241a11c9849c590cf7a4b257b04caaefc42 Mon Sep 17 00:00:00 2001 From: Lior Keren Date: Tue, 22 Nov 2022 20:07:05 +0200 Subject: [PATCH 06/10] removing react-native from the jest config file --- jest.config.js | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/jest.config.js b/jest.config.js index 1860bb7..0280af0 100644 --- a/jest.config.js +++ b/jest.config.js @@ -183,16 +183,7 @@ module.exports = { // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation // transformIgnorePatterns: ['/node_modules/', '\\.pnp\\.[^\\/]+$'] - transformIgnorePatterns: [ - 'node_modules/(?!(react-native' + - '|react-navigation-tabs' + - '|react-native-splash-screen' + - '|react-native-screens' + - '|react-native-reanimated' + - ')/)', - 'node_modules/(?!(jest-)?react-native|react-(native|universal|navigation)-(.*)|@react-native-community/(.*)|@react-navigation/(.*)|bs-platform|(@[a-zA-Z]+/)?(bs|reason|rescript)-(.*)+)', - '/node_modules/' - ] + transformIgnorePatterns: ['/node_modules/'] // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them // unmockedModulePathPatterns: undefined, From b244c4551fece1c31bac71b016495e211f76bc12 Mon Sep 17 00:00:00 2001 From: Lior Keren Date: Wed, 23 Nov 2022 15:03:50 +0200 Subject: [PATCH 07/10] GuitarGallery test cleanup --- app/javascript/components/GuitarGallery.js | 10 +++++- .../components/tests/GuitarGallery.test.js | 32 +++++++++++++++---- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/app/javascript/components/GuitarGallery.js b/app/javascript/components/GuitarGallery.js index 4a51896..eac903c 100644 --- a/app/javascript/components/GuitarGallery.js +++ b/app/javascript/components/GuitarGallery.js @@ -30,6 +30,7 @@ const GuitarGallery = () => { Guitars For Sale: { ) : ( guitars.map(({ name, url, price, description }) => ( - + )) )} diff --git a/app/javascript/components/tests/GuitarGallery.test.js b/app/javascript/components/tests/GuitarGallery.test.js index 5bc33ec..a8fb076 100644 --- a/app/javascript/components/tests/GuitarGallery.test.js +++ b/app/javascript/components/tests/GuitarGallery.test.js @@ -1,15 +1,18 @@ import React from 'react'; -import { render, screen /*, waitForElement*/ } from '@testing-library/react'; +import { waitFor, render, screen /*, waitForElement*/ } from '@testing-library/react'; import '@testing-library/jest-dom/extend-expect'; import axios from 'axios'; import GuitarGallery from '../GuitarGallery.js'; import { guitarsUrl } from '../constants'; import { guitarData } from './fixtures'; +// import { act } from 'react-dom/test-utils/index.js'; test("show loader when it's fetching data, then render Guitars", async () => { - axios.get.mockResolvedValueOnce(guitarData); + await axios.get.mockResolvedValueOnce(guitarData); //show loader - const { unmount /*, getAllByTestId, getByText*/ } = render(); + // act(() => { + // }); + const { unmount /*, getAllByTestId*/, getByText, getByLabelText } = render(); // expect(getByText(/loading.../i)).toBeInTheDocument(); // check the correct url is called: @@ -35,11 +38,28 @@ test("show loader when it's fetching data, then render Guitars", async () => { // }); // ensure the title of the page is presented in the page: - expect(screen.getByText('Guitars For Sale:')).toBeInTheDocument(); + expect(screen.getByText('Guitars For Sale:')).toBeInTheDocument(); // -> passes // ensure some content of the page is presented in the page: - expect(screen.getByText('some description in guitar1_test')).toBeInTheDocument(); - expect(screen.getByText('some description in guitar2_test')).toBeInTheDocument(); + + // Option 2: + const title = getByText(/Guitars For Sale:/); + expect(title).toHaveTextContent('Guitars For Sale:'); // -> passes + + // // fails: + // const galleryItem = getByLabelText(/galleryItem/); + // expect(galleryItem).toHaveTextContent('Description: some description in guitar1_test'); + + // const gallery = getByLabelText(/gallery/); + // expect(gallery).toHaveTextContent('Description: some description in guitar1_test'); + + // Option 1: + // expect(screen.getByRole('GuitarInfo')).toHaveTextContent('some description in guitar1_test'); + await waitFor(() => screen.getByText('Description: some description in guitar1_test')); + // await waitFor(() => screen.getByText('Description: some description in guitar2_test')); + + expect(screen.getByText('Description: some description in guitar1_test')).toBeInTheDocument(); + expect(screen.getByText('Description: some description in guitar2_test')).toBeInTheDocument(); // unmnount the component from the DOM unmount(); From 942666a18c67e712f8fcf3394787dbe51bde2518 Mon Sep 17 00:00:00 2001 From: Lior Keren Date: Wed, 23 Nov 2022 16:32:09 +0200 Subject: [PATCH 08/10] removing unnecessary comments from GuitarGallery test --- app/javascript/components/GuitarGallery.js | 10 +---- .../components/tests/GuitarGallery.test.js | 45 +++---------------- 2 files changed, 7 insertions(+), 48 deletions(-) diff --git a/app/javascript/components/GuitarGallery.js b/app/javascript/components/GuitarGallery.js index eac903c..4a51896 100644 --- a/app/javascript/components/GuitarGallery.js +++ b/app/javascript/components/GuitarGallery.js @@ -30,7 +30,6 @@ const GuitarGallery = () => { Guitars For Sale: { ) : ( guitars.map(({ name, url, price, description }) => ( - + )) )} diff --git a/app/javascript/components/tests/GuitarGallery.test.js b/app/javascript/components/tests/GuitarGallery.test.js index a8fb076..d06fb40 100644 --- a/app/javascript/components/tests/GuitarGallery.test.js +++ b/app/javascript/components/tests/GuitarGallery.test.js @@ -1,62 +1,29 @@ import React from 'react'; -import { waitFor, render, screen /*, waitForElement*/ } from '@testing-library/react'; +import { waitFor, render, screen } from '@testing-library/react'; import '@testing-library/jest-dom/extend-expect'; import axios from 'axios'; import GuitarGallery from '../GuitarGallery.js'; import { guitarsUrl } from '../constants'; import { guitarData } from './fixtures'; -// import { act } from 'react-dom/test-utils/index.js'; test("show loader when it's fetching data, then render Guitars", async () => { await axios.get.mockResolvedValueOnce(guitarData); - //show loader - // act(() => { - // }); - const { unmount /*, getAllByTestId*/, getByText, getByLabelText } = render(); - // expect(getByText(/loading.../i)).toBeInTheDocument(); + const { unmount, getByText } = render(); // check the correct url is called: expect(axios.get).toHaveBeenCalledWith(guitarsUrl); expect(axios.get).toHaveBeenCalledTimes(1); - // //check what's rendered in the row - // const rowValues = await waitForElement(() => getAllByTestId('row').map((row) => row.textContent)); - // expect(rowValues).toEqual(['ali', 'abu']); - - // const guitarDescriptions = await screen.findAllByText(/Description/i); - // // const guitarDescriptions = screen.findAllBy('Description'); - // const expectedDescription = [ - // 'some description in guitar1_test', - // 'some description in guitar2_test' - // ]; - // // option 1: - // expect(guitarDescriptions).toEqual(expectedDescription); - // option 2: - // console.log(guitarDescriptions); - // guitarDescriptions.forEach((description, index) => { - // expect(description).toBe(expectedDescription.at(index)); - // }); - // ensure the title of the page is presented in the page: - expect(screen.getByText('Guitars For Sale:')).toBeInTheDocument(); // -> passes - - // ensure some content of the page is presented in the page: + expect(screen.getByText('Guitars For Sale:')).toBeInTheDocument(); - // Option 2: + // Another option to test it using getByText that react-testing-library/render returns: const title = getByText(/Guitars For Sale:/); - expect(title).toHaveTextContent('Guitars For Sale:'); // -> passes - - // // fails: - // const galleryItem = getByLabelText(/galleryItem/); - // expect(galleryItem).toHaveTextContent('Description: some description in guitar1_test'); + expect(title).toHaveTextContent('Guitars For Sale:'); - // const gallery = getByLabelText(/gallery/); - // expect(gallery).toHaveTextContent('Description: some description in guitar1_test'); + // ensure some content of the page is presented in the page: - // Option 1: - // expect(screen.getByRole('GuitarInfo')).toHaveTextContent('some description in guitar1_test'); await waitFor(() => screen.getByText('Description: some description in guitar1_test')); - // await waitFor(() => screen.getByText('Description: some description in guitar2_test')); expect(screen.getByText('Description: some description in guitar1_test')).toBeInTheDocument(); expect(screen.getByText('Description: some description in guitar2_test')).toBeInTheDocument(); From 6dfb18c2401b034d710f52b4b690dcf043a6707a Mon Sep 17 00:00:00 2001 From: Lior Keren Date: Wed, 23 Nov 2022 18:42:19 +0200 Subject: [PATCH 09/10] rubocop ignore guitarController method length --- app/controllers/guitars_controller.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/controllers/guitars_controller.rb b/app/controllers/guitars_controller.rb index 3eb1cc4..7a0aab9 100644 --- a/app/controllers/guitars_controller.rb +++ b/app/controllers/guitars_controller.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +# rubocop:disable Metrics/MethodLength + class GuitarsController < ApplicationController def index render json: [ @@ -24,3 +26,5 @@ def index ] end end + +# rubocop:enable Metrics/MethodLength From 7b0fb3cbc99c6aa870f7d777a0aa3b8faa572a0b Mon Sep 17 00:00:00 2001 From: Lior Keren Date: Wed, 23 Nov 2022 18:42:32 +0200 Subject: [PATCH 10/10] removing mock data from __mocks__/axios.js file --- app/__mocks__/axios.js | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/app/__mocks__/axios.js b/app/__mocks__/axios.js index acc7247..476666f 100644 --- a/app/__mocks__/axios.js +++ b/app/__mocks__/axios.js @@ -1,18 +1,6 @@ export default { - get: jest.fn().mockResolvedValue({ - data: [ - { - name: 'guitar1_test', - url: 'https://rukminim1.flixcart.com/image/416/416/acoustic-guitar/x/8/w/topaz-blue-signature-original-imaefec7uhypjdr9.jpeg?q=70', - price: '100', - description: 'some description in guitar1_test' - }, - { - name: 'guitar2_test', - url: 'https://shop.brianmayguitars.co.uk/user/special/content/Antique%20Cherry%20a.jpg', - price: '200', - description: 'some description in guitar2_test' - } - ] - }) + get: jest.fn(), + post: jest.fn(), + put: jest.fn(), + delete: jest.fn() };