From ce389af9b3e60e3f829d19311a4c3178bb348b02 Mon Sep 17 00:00:00 2001 From: itaich Date: Mon, 18 Jan 2021 11:18:01 +0200 Subject: [PATCH 1/5] Add clone functionality to server and UI --- README.md | 9 ++- client/src/App.scss | 19 ++++++- client/src/App.tsx | 120 +++++++++++++++++++++------------------ client/src/api.ts | 10 +++- images/clone.png | Bin 0 -> 18796 bytes server/index.ts | 18 +++++- server/package-lock.json | 18 +++++- server/package.json | 6 +- tester/e2e.test.ts | 24 ++++++++ 9 files changed, 160 insertions(+), 64 deletions(-) create mode 100644 images/clone.png diff --git a/README.md b/README.md index e8f3b90..9c086e0 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,14 @@ Otherwise, it is mandatory. a. Add at least 3 automated browser tests using puppeteer, testing key features of your choice. b. Add component tests (using `jest`) to your work from *part 1*. - +### Part 5 - Add A Clone Button +It's pretty common that we need to clone tickets in our system. +### Part 1 +Add a clone button on the menu item and replicate an item in the client +![CloneButton](./images/clone.png) +### Part 2 +Add a clone method in the API and server and add an in memory item to the collection + ## General notes - Test your work well. Think of edge cases. Think of how users will use it, and make sure your work is of high quality - Stick to the best practices of the libraries used as much as possible diff --git a/client/src/App.scss b/client/src/App.scss index 0e92993..e2e9507 100644 --- a/client/src/App.scss +++ b/client/src/App.scss @@ -89,7 +89,24 @@ body { font-style: normal; color: #20455e; } - + + .cloneButton{ + position: absolute; + right: 10px; + top: 10px; + box-shadow: 0 2px 6px 1px #e1e5e8; + border:none; + padding: 5px; + background-color: transparent; + border-radius:5px; + cursor: pointer; + outline: none; + + &:active { + box-shadow: 0 2px 6px 1px #20455e; + color: red; + } + } footer { display: flex; diff --git a/client/src/App.tsx b/client/src/App.tsx index c9b6ab3..3fcb085 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -3,65 +3,77 @@ import './App.scss'; import {createApiClient, Ticket} from './api'; export type AppState = { - tickets?: Ticket[], - search: string; + tickets?: Ticket[], + search: string; } const api = createApiClient(); export class App extends React.PureComponent<{}, AppState> { - state: AppState = { - search: '' - } - - searchDebounce: any = null; - - async componentDidMount() { - this.setState({ - tickets: await api.getTickets() - }); - } - - renderTickets = (tickets: Ticket[]) => { - - const filteredTickets = tickets - .filter((t) => (t.title.toLowerCase() + t.content.toLowerCase()).includes(this.state.search.toLowerCase())); - - - return (); - } - - onSearch = async (val: string, newPage?: number) => { - - clearTimeout(this.searchDebounce); - - this.searchDebounce = setTimeout(async () => { - this.setState({ - search: val - }); - }, 300); - } - - render() { - const {tickets} = this.state; - - return (
-

Tickets List

-
- this.onSearch(e.target.value)}/> -
- {tickets ?
Showing {tickets.length} results
: null } - {tickets ? this.renderTickets(tickets) :

Loading..

} -
) - } + state: AppState = { + search: '' + } + + searchDebounce: any = null; + + async componentDidMount() { + this.setState({ + tickets: await api.getTickets() + }); + } + + cloneTicket = async (id: string) => { + const newTicket : Ticket = await api.cloneTicket(id); + if(newTicket){ + let { tickets } = this.state; + tickets = tickets ? tickets : []; + tickets = [newTicket].concat(tickets); + this.setState({tickets}); + } + } + + renderTickets = (tickets: Ticket[]) => { + + const filteredTickets = tickets + .filter((t) => (t.title.toLowerCase() + t.content.toLowerCase()).includes(this.state.search.toLowerCase())); + + + return (); + } + + onSearch = async (val: string, newPage?: number) => { + + clearTimeout(this.searchDebounce); + + this.searchDebounce = setTimeout(async () => { + this.setState({ + search: val + }); + }, 300); + } + + render() { + const {tickets} = this.state; + + return (
+

Tickets List

+
+ this.onSearch(e.target.value)}/> +
+ {tickets ?
Showing {tickets.length} results
: null} + {tickets ? this.renderTickets(tickets) :

Loading..

} +
) + } } -export default App; \ No newline at end of file +export default App; diff --git a/client/src/api.ts b/client/src/api.ts index 6fa32b2..1b45d11 100644 --- a/client/src/api.ts +++ b/client/src/api.ts @@ -18,6 +18,12 @@ export const createApiClient = (): ApiClient => { return { getTickets: () => { return axios.get(APIRootPath).then((res) => res.data); - } - } + }, + cloneTicket: (id: string) => { + return axios.post(`${APIRootPath}/${id}/clone`).then((res) => res.data); + } + } } + + + diff --git a/images/clone.png b/images/clone.png new file mode 100644 index 0000000000000000000000000000000000000000..d6967406621979fe99ddd9b0bc7386332b771cd7 GIT binary patch literal 18796 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

^B0NGb+AM$`W2XAO3xf^i`R&n6Xp@)9XxXmF% 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 E4{LtP1^@s6 literal 0 HcmV?d00001 diff --git a/server/index.ts b/server/index.ts index f9c07f7..d9a9457 100644 --- a/server/index.ts +++ b/server/index.ts @@ -1,4 +1,5 @@ import express from 'express'; +import {v4 as uuidV4 } from 'uuid'; import bodyParser = require('body-parser'); import { tempData } from './temp-data'; import { serverAPIPort, APIPath } from '@fed-exam/config'; @@ -28,6 +29,21 @@ app.get(APIPath, (req, res) => { res.send(paginatedData); }); +app.post('/api/tickets/:id/clone', (req, res) => { + let newTicket; + const ticket = tempData.filter(ticket => ticket.id === req.params.id) + if (ticket) { + newTicket = JSON.parse(JSON.stringify(ticket[0])); + newTicket.id = uuidV4(); + tempData.push(newTicket); + } + if(newTicket) { + res.send(newTicket); + }else { + res.status(500); + res.send('Failed to find ticket to clone'); + } +}); + app.listen(serverAPIPort); console.log('server running', serverAPIPort) - diff --git a/server/package-lock.json b/server/package-lock.json index 5bfbed9..9a2427e 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -82,6 +82,11 @@ "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==" }, + "@types/uuid": { + "version": "8.3.0", + "resolved": "https://repo.dev.wixpress.com/artifactory/api/npm/npm-repos/@types/uuid/-/uuid-8.3.0.tgz", + "integrity": "sha1-IVwjHf9zbVupJBDm1gIFDM5+Jz8=" + }, "abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -513,6 +518,13 @@ "qs": "^6.5.2", "url-template": "^2.0.8", "uuid": "^3.2.1" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://repo.dev.wixpress.com/artifactory/api/npm/npm-repos/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha1-sj5DWK+oogL+ehAK8fX4g/AgB+4=" + } } }, "graceful-fs": { @@ -1251,9 +1263,9 @@ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "uuid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", - "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" + "version": "8.3.2", + "resolved": "https://repo.dev.wixpress.com/artifactory/api/npm/npm-repos/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha1-gNW1ztJxu5r2xEXyGhoExgbO++I=" }, "validate-npm-package-license": { "version": "3.0.4", diff --git a/server/package.json b/server/package.json index dace8dd..4765927 100644 --- a/server/package.json +++ b/server/package.json @@ -20,7 +20,9 @@ "googleapis": "^39.2.0", "ts-node": "^8.3.0", "typescript": "^3.5.3", - "ts-node-dev": "1.1.0" - }, + "ts-node-dev": "1.1.0", + "uuid": "latest" + + }, "description": "" } diff --git a/tester/e2e.test.ts b/tester/e2e.test.ts index 9f28784..9317530 100644 --- a/tester/e2e.test.ts +++ b/tester/e2e.test.ts @@ -52,3 +52,27 @@ describe("Titles", () => { }); +describe("Clone", () => { + + test('20 clone buttons are rendered', async () => { + await goToMainPage(); + const cloneButtons = await page.$$('.cloneButton') + expect(cloneButtons.length).toBe(20) + }); + + test('Clone button text', async () => { + await goToMainPage(); + const cloneButtons = await page.$$('.cloneButton') + let value = await page.evaluate(el => el.textContent, cloneButtons[0]) + expect(value).toBe('Clone') + }); + + test('Clone an item', async () => { + await goToMainPage(); + const cloneButtons = await page.$$('.cloneButton') + await cloneButtons[0].click(); + const titles = await page.$$('.title') + expect(titles.length).toBe(21) + }); +}); + From 1f67cd4125774df8892524523a06e0234ac313ed Mon Sep 17 00:00:00 2001 From: Hadar Geva Date: Sat, 23 Jan 2021 15:50:06 +0200 Subject: [PATCH 2/5] wip --- client/src/App.scss | 6 +- client/src/api.ts | 33 ++-- package-lock.json | 2 +- server/package-lock.json | 323 +++++++++++++++++++++++-------------- server/package.json | 44 ++--- tester/cloneAnItem.test.ts | 55 +++++++ tester/e2e.test.ts | 26 +-- 7 files changed, 305 insertions(+), 184 deletions(-) create mode 100644 tester/cloneAnItem.test.ts diff --git a/client/src/App.scss b/client/src/App.scss index e2e9507..08320f3 100644 --- a/client/src/App.scss +++ b/client/src/App.scss @@ -3,7 +3,7 @@ body { background-color: #f5f9fc; padding-top: 100px; - + padding: 50px; main { @@ -111,7 +111,7 @@ body { display: flex; justify-content: space-between; - + .meta-data { color: #7a92a5; font-size: 12px; @@ -120,7 +120,7 @@ body { } } - + } } } diff --git a/client/src/api.ts b/client/src/api.ts index 1b45d11..8ca72a5 100644 --- a/client/src/api.ts +++ b/client/src/api.ts @@ -1,28 +1,29 @@ import axios from 'axios'; -import {APIRootPath} from '@fed-exam/config'; +import { APIRootPath } from '@fed-exam/config'; export type Ticket = { - id: string, - title: string; - content: string; - creationTime: number; - userEmail: string; - labels?: string[]; + id: string, + title: string; + content: string; + creationTime: number; + userEmail: string; + labels?: string[]; } export type ApiClient = { - getTickets: () => Promise; + getTickets: () => Promise; + cloneTicket: (id: string) => Promise; } export const createApiClient = (): ApiClient => { - return { - getTickets: () => { - return axios.get(APIRootPath).then((res) => res.data); - }, - cloneTicket: (id: string) => { - return axios.post(`${APIRootPath}/${id}/clone`).then((res) => res.data); - } - } + return { + getTickets: () => { + return axios.get(APIRootPath).then((res) => res.data); + }, + cloneTicket: (id: string) => { + return axios.post(`${APIRootPath}/${id}/clone`).then((res) => res.data); + } + } } diff --git a/package-lock.json b/package-lock.json index 99c5edc..21baffc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "fed-entry-level-exam", + "name": "fed-entry-level-exam-root", "version": "1.0.0", "lockfileVersion": 1, "requires": true, diff --git a/server/package-lock.json b/server/package-lock.json index 9a2427e..668a5ed 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -4,9 +4,6 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "@fed-exam/config": { - "version": "file:../configuration" - }, "@types/body-parser": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.1.tgz", @@ -84,8 +81,8 @@ }, "@types/uuid": { "version": "8.3.0", - "resolved": "https://repo.dev.wixpress.com/artifactory/api/npm/npm-repos/@types/uuid/-/uuid-8.3.0.tgz", - "integrity": "sha1-IVwjHf9zbVupJBDm1gIFDM5+Jz8=" + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz", + "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==" }, "abort-controller": { "version": "3.0.0", @@ -112,6 +109,15 @@ "es6-promisify": "^5.0.0" } }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, "arg": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.1.tgz", @@ -142,6 +148,11 @@ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz", "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==" }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" + }, "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", @@ -168,6 +179,14 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, "buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -202,6 +221,21 @@ "resolved": "https://registry.npmjs.org/chance/-/chance-1.1.3.tgz", "integrity": "sha512-XeJsdoVAzDb1WRPRuMBesRSiWpW1uNTo5Fd7mYxPJsAfgX71+jfuCOHOdbyBz2uAUZ8TwKcXgWk3DMedFfJkbg==" }, + "chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.3.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -230,6 +264,11 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, "currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", @@ -247,11 +286,6 @@ "meow": "^3.3.0" } }, - "debounce": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.0.tgz", - "integrity": "sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg==" - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -389,12 +423,12 @@ "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.0.tgz", "integrity": "sha512-R9bHCvweUxxwkDwhjav5vxpFvdPGlVngtqmx4pIZfSUhM/Q4NiIUHB456BAf+Q1Nwu3HEZYONtu+Rya+af4jiQ==" }, - "filewatcher": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/filewatcher/-/filewatcher-3.0.1.tgz", - "integrity": "sha1-9KGVc1Xdr0Q8zXiolfPVXiPIoDQ=", + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "requires": { - "debounce": "^1.0.0" + "to-regex-range": "^5.0.1" } }, "finalhandler": { @@ -435,6 +469,17 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "fsevents": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.1.tgz", + "integrity": "sha512-YR47Eg4hChJGAB1O3yEAOkGO+rlzutoICGqGo9EZ4lKWokzZRSyIW1QmTzqjtw8MJdj9srP869CuWw/hyzSiBw==", + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, "gaxios": { "version": "1.8.4", "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-1.8.4.tgz", @@ -473,6 +518,14 @@ "path-is-absolute": "^1.0.0" } }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "requires": { + "is-glob": "^4.0.1" + } + }, "google-auth-library": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-3.1.2.tgz", @@ -528,14 +581,9 @@ } }, "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" - }, - "growly": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", - "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=" + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" }, "gtoken": { "version": "2.3.3", @@ -556,10 +604,18 @@ } } }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, "hosted-git-info": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz", - "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==" + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==" }, "http-errors": { "version": "1.7.2", @@ -637,29 +693,50 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-core-module": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "requires": { + "has": "^1.0.3" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==" + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", "requires": { - "number-is-nan": "^1.0.0" + "is-extglob": "^2.1.1" } }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" }, - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, "json-bigint": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.0.tgz", @@ -792,24 +869,14 @@ } }, "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - } - } + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" }, "ms": { "version": "2.0.0", @@ -831,18 +898,6 @@ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.8.5.tgz", "integrity": "sha512-vFMQIWt+J/7FLNyKouZ9TazT74PRV3wgv9UT4cRjC8BffxFbKXkgIWR42URCPSnHm/QDz6BOlb2Q0U4+VQT67Q==" }, - "node-notifier": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.3.tgz", - "integrity": "sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q==", - "requires": { - "growly": "^1.3.0", - "is-wsl": "^1.1.0", - "semver": "^5.5.0", - "shellwords": "^0.1.1", - "which": "^1.3.0" - } - }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -854,10 +909,10 @@ "validate-npm-package-license": "^3.0.1" } }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" }, "object-assign": { "version": "4.1.1", @@ -933,6 +988,11 @@ } } }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==" + }, "pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", @@ -1000,6 +1060,14 @@ "read-pkg": "^1.0.0" } }, + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "requires": { + "picomatch": "^2.2.1" + } + }, "redent": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", @@ -1018,10 +1086,11 @@ } }, "resolve": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", - "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", "requires": { + "is-core-module": "^2.1.0", "path-parse": "^1.0.6" } }, @@ -1091,15 +1160,10 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" }, - "shellwords": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", - "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==" - }, "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" }, "source-map": { "version": "0.6.1", @@ -1116,32 +1180,32 @@ } }, "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", "requires": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" } }, "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" }, "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "requires": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==" + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", + "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==" }, "statuses": { "version": "1.5.0", @@ -1169,15 +1233,23 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + }, "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, "tree-kill": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.1.tgz", - "integrity": "sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q==" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==" }, "trim-newlines": { "version": "1.0.0", @@ -1197,22 +1269,47 @@ } }, "ts-node-dev": { - "version": "1.0.0-pre.44", - "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.0.0-pre.44.tgz", - "integrity": "sha512-M5ZwvB6FU3jtc70i5lFth86/6Qj5XR5nMMBwVxZF4cZhpO7XcbWw6tbNiJo22Zx0KfjEj9py5DANhwLOkPPufw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.1.0.tgz", + "integrity": "sha512-1h/2IOEbGsmzW0bjG33yj6Xndic9IsqmGbd9u9AwvUXXZm3SSImNh9zdZfkDJdV6SZBYEb+lAQrjLvtSfgEvNQ==", "requires": { + "chokidar": "^3.4.0", "dateformat": "~1.0.4-1.2.3", "dynamic-dedupe": "^0.3.0", - "filewatcher": "~3.0.0", - "minimist": "^1.1.3", - "mkdirp": "^0.5.1", - "node-notifier": "^5.4.0", + "minimist": "^1.2.5", + "mkdirp": "^1.0.4", "resolve": "^1.0.0", "rimraf": "^2.6.1", "source-map-support": "^0.5.12", - "tree-kill": "^1.2.1", - "ts-node": "*", + "tree-kill": "^1.2.2", + "ts-node": "^9.0.0", "tsconfig": "^7.0.0" + }, + "dependencies": { + "ts-node": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "requires": { + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + }, + "dependencies": { + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + } + } + } } }, "tsconfig": { @@ -1264,8 +1361,8 @@ }, "uuid": { "version": "8.3.2", - "resolved": "https://repo.dev.wixpress.com/artifactory/api/npm/npm-repos/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha1-gNW1ztJxu5r2xEXyGhoExgbO++I=" + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" }, "validate-npm-package-license": { "version": "3.0.4", @@ -1281,14 +1378,6 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "requires": { - "isexe": "^2.0.0" - } - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/server/package.json b/server/package.json index 4765927..934b58e 100644 --- a/server/package.json +++ b/server/package.json @@ -1,28 +1,28 @@ { - "name": "@fed-exam/server", - "version": "1.0.0", - "main": "index.js", + "name": "@fed-exam/server", + "version": "1.0.0", + "main": "index.js", "scripts": { "start": "ts-node-dev index.ts", "d": "ts-node index.ts" }, - "author": "", - "license": "ISC", - "dependencies": { - "@fed-exam/config": "^1.0.0", - "@types/body-parser": "^1.17.1", - "@types/chance": "^1.0.6", - "@types/express": "^4.17.1", - "@types/node": "^12.7.2", - "body-parser": "^1.19.0", - "chance": "^1.1.0", - "express": "^4.17.1", - "googleapis": "^39.2.0", - "ts-node": "^8.3.0", - "typescript": "^3.5.3", - "ts-node-dev": "1.1.0", - "uuid": "latest" - - }, - "description": "" + "author": "", + "license": "ISC", + "dependencies": { + "@fed-exam/config": "^1.0.0", + "@types/body-parser": "^1.17.1", + "@types/chance": "^1.0.6", + "@types/express": "^4.17.1", + "@types/node": "^12.7.2", + "body-parser": "^1.19.0", + "chance": "^1.1.0", + "express": "^4.17.1", + "googleapis": "^39.2.0", + "ts-node": "^8.3.0", + "typescript": "^3.5.3", + "ts-node-dev": "1.1.0", + "uuid": "latest", + "@types/uuid": "latest" + }, + "description": "" } diff --git a/tester/cloneAnItem.test.ts b/tester/cloneAnItem.test.ts new file mode 100644 index 0000000..d32eb6c --- /dev/null +++ b/tester/cloneAnItem.test.ts @@ -0,0 +1,55 @@ +const puppeteer = require('puppeteer'); +import { staticsUrl } from '@fed-exam/config'; + +let browser; +let page; + +beforeAll(async () => { + browser = await puppeteer.launch(); + page = await browser.newPage(); + await page.setViewport({ + width: 1280, + height: 1080, + deviceScaleFactor: 1, + }); +}) + +afterAll(async () => { + await browser.close(); +}) + +const goToMainPage = async () => { + await page.goto(staticsUrl); + //await page.screenshot({ path: 'main_page.png' }); +} + +describe("Clone", () => { + + test('20 items with "clone" text are rendered', async () => { + await goToMainPage(); + const cloneButtons = await page.$x("//*[contains(text(), 'clone') or contains(text(), 'Clone')]"); + + expect(cloneButtons.length).toBe(20) + }); + + test('Click on Clone element add new item', async () => { + await goToMainPage(); + const cloneButtons = await page.$x("//*[contains(text(), 'clone') or contains(text(), 'Clone')]"); + await cloneButtons[0].click(); + const titles = await page.$$('.title') + expect(titles.length).toBe(21) + }); + + test('Clone click clones the right data', async () => { + await goToMainPage(); + const cloneButtons = await page.$x("//*[contains(text(), 'clone') or contains(text(), 'Clone')]"); + await cloneButtons[0].click(); + const titles = await page.$$('.title') + + let value0 = await page.evaluate(el => el.textContent, titles[0]) + let value20 = await page.evaluate(el => el.textContent, titles[20]) + expect(value0).toBe(value20) + }); + +}); + diff --git a/tester/e2e.test.ts b/tester/e2e.test.ts index 9317530..054f5eb 100644 --- a/tester/e2e.test.ts +++ b/tester/e2e.test.ts @@ -1,6 +1,6 @@ const puppeteer = require('puppeteer'); const serverData = require('../server/data.json'); -import { staticsUrl } from '@fed-exa/config'; +import { staticsUrl } from '@fed-exam/config'; let browser; let page; @@ -52,27 +52,3 @@ describe("Titles", () => { }); -describe("Clone", () => { - - test('20 clone buttons are rendered', async () => { - await goToMainPage(); - const cloneButtons = await page.$$('.cloneButton') - expect(cloneButtons.length).toBe(20) - }); - - test('Clone button text', async () => { - await goToMainPage(); - const cloneButtons = await page.$$('.cloneButton') - let value = await page.evaluate(el => el.textContent, cloneButtons[0]) - expect(value).toBe('Clone') - }); - - test('Clone an item', async () => { - await goToMainPage(); - const cloneButtons = await page.$$('.cloneButton') - await cloneButtons[0].click(); - const titles = await page.$$('.title') - expect(titles.length).toBe(21) - }); -}); - From c8494060e784302758439b205961968b5e268f13 Mon Sep 17 00:00:00 2001 From: Hadar Geva Date: Sat, 23 Jan 2021 16:34:59 +0200 Subject: [PATCH 3/5] fix tests --- tester/cloneAnItem.test.ts | 44 +++++++++++++++++++++++-------------- tester/getElementsByText.ts | 2 ++ tester/sleep.ts | 6 +++++ 3 files changed, 35 insertions(+), 17 deletions(-) create mode 100644 tester/sleep.ts diff --git a/tester/cloneAnItem.test.ts b/tester/cloneAnItem.test.ts index d32eb6c..43225a0 100644 --- a/tester/cloneAnItem.test.ts +++ b/tester/cloneAnItem.test.ts @@ -1,40 +1,45 @@ -const puppeteer = require('puppeteer'); +import { getElementsByText } from "./getElementsByText"; import { staticsUrl } from '@fed-exam/config'; +import { sleep } from "./sleep"; + +const puppeteer = require('puppeteer'); let browser; let page; beforeAll(async () => { - browser = await puppeteer.launch(); - page = await browser.newPage(); - await page.setViewport({ - width: 1280, - height: 1080, - deviceScaleFactor: 1, - }); + browser = await puppeteer.launch(); + page = await browser.newPage(); + await page.setViewport({ + width: 1280, + height: 1080, + deviceScaleFactor: 1, + }); }) afterAll(async () => { - await browser.close(); + await browser.close(); }) const goToMainPage = async () => { - await page.goto(staticsUrl); - //await page.screenshot({ path: 'main_page.png' }); + await page.goto(staticsUrl); + //await page.screenshot({ path: 'main_page.png' }); } -describe("Clone", () => { +describe("Clone an item", () => { + const cloneButtonTexts = ['clone', 'Clone']; test('20 items with "clone" text are rendered', async () => { await goToMainPage(); - const cloneButtons = await page.$x("//*[contains(text(), 'clone') or contains(text(), 'Clone')]"); + const cloneButtons = await getElementsByText(cloneButtonTexts, page) expect(cloneButtons.length).toBe(20) }); test('Click on Clone element add new item', async () => { await goToMainPage(); - const cloneButtons = await page.$x("//*[contains(text(), 'clone') or contains(text(), 'Clone')]"); + const cloneButtons = await getElementsByText(cloneButtonTexts, page) + await cloneButtons[0].click(); const titles = await page.$$('.title') expect(titles.length).toBe(21) @@ -42,13 +47,18 @@ describe("Clone", () => { test('Clone click clones the right data', async () => { await goToMainPage(); - const cloneButtons = await page.$x("//*[contains(text(), 'clone') or contains(text(), 'Clone')]"); + const cloneButtons = await getElementsByText(cloneButtonTexts, page) + + expect(cloneButtons.length).toBe(20) + await cloneButtons[0].click(); + await sleep(200); + const titles = await page.$$('.title') let value0 = await page.evaluate(el => el.textContent, titles[0]) - let value20 = await page.evaluate(el => el.textContent, titles[20]) - expect(value0).toBe(value20) + let value1 = await page.evaluate(el => el.textContent, titles[1]) + expect(value0).toBe(value1) }); }); diff --git a/tester/getElementsByText.ts b/tester/getElementsByText.ts index 6d3a612..ecf90f3 100644 --- a/tester/getElementsByText.ts +++ b/tester/getElementsByText.ts @@ -1,4 +1,5 @@ export const getElementsByText = async (textOptions, page) => { + textOptions = [...textOptions] let query = `//*[contains(text(), '${textOptions[0]}')` textOptions.splice(1).forEach(option => { query += ` or contains(text(), '${option}')` @@ -10,3 +11,4 @@ export const getElementsByText = async (textOptions, page) => { return elements } + diff --git a/tester/sleep.ts b/tester/sleep.ts new file mode 100644 index 0000000..323418a --- /dev/null +++ b/tester/sleep.ts @@ -0,0 +1,6 @@ +export const sleep = (millisecondsCount) => { + if (!millisecondsCount) { + return; + } + return new Promise(resolve => setTimeout(resolve, millisecondsCount)).catch(); +} From 85b3214fcf4c441296f70c488ad2e465f940cbaf Mon Sep 17 00:00:00 2001 From: hadarge Date: Sat, 23 Jan 2021 16:49:59 +0200 Subject: [PATCH 4/5] Update README.md --- README.md | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 9c086e0..2f0e910 100644 --- a/README.md +++ b/README.md @@ -54,8 +54,13 @@ d. **Bonus** Step *a* wasn't enough - some tickets have long content. Add a show ### Part 2 - List functionality -a. Agents are complaining that our search functionality isn't working properly. They gave the example that when searching for "wix store", the ticket titled "Search bar for my wix store" (id `6860d043-f551-58c8-84d6-f9e6a8cb0cb2`) is not returned. Checking the data, that ticket does exist.. Find the issue and fix it. -Friendly reminder to commit and push after completing this step. +2a. It's pretty common that we need to clone tickets in our system. +#### Part 1 +Add a clone button or text that will replicate an item in the client. + +#### Part 2 +Add a clone method in the API and server and add an in memory item to the collection +Connect your client side "clone" button to that API call b. We're showing only 20 tickets but agents can swear there are more. Solve this problem. **Keep in mind the number of tickets is planned to grow exponentially very soon so make sure to think of a proper solution.** @@ -82,14 +87,6 @@ Otherwise, it is mandatory. a. Add at least 3 automated browser tests using puppeteer, testing key features of your choice. b. Add component tests (using `jest`) to your work from *part 1*. - -### Part 5 - Add A Clone Button -It's pretty common that we need to clone tickets in our system. -### Part 1 -Add a clone button on the menu item and replicate an item in the client -![CloneButton](./images/clone.png) -### Part 2 -Add a clone method in the API and server and add an in memory item to the collection ## General notes - Test your work well. Think of edge cases. Think of how users will use it, and make sure your work is of high quality From 095901ab185b3c9c85d3335225e83592784a48e9 Mon Sep 17 00:00:00 2001 From: hadarge Date: Sat, 23 Jan 2021 16:51:07 +0200 Subject: [PATCH 5/5] Update README.md --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2f0e910..64564a8 100644 --- a/README.md +++ b/README.md @@ -55,12 +55,10 @@ d. **Bonus** Step *a* wasn't enough - some tickets have long content. Add a show ### Part 2 - List functionality 2a. It's pretty common that we need to clone tickets in our system. -#### Part 1 -Add a clone button or text that will replicate an item in the client. -#### Part 2 -Add a clone method in the API and server and add an in memory item to the collection -Connect your client side "clone" button to that API call +1.Add a clone button or text that will replicate an item in the client. +2.Add a clone method in the API and server and add an in memory item to the collection +3.Connect your client side "clone" button to that API call b. We're showing only 20 tickets but agents can swear there are more. Solve this problem. **Keep in mind the number of tickets is planned to grow exponentially very soon so make sure to think of a proper solution.**