├── .github ├── FUNDING.yml └── workflows │ ├── blockchain.app.yml │ ├── dev-portfolio.yml │ ├── master_nikolay-react.yml │ └── web2022.yml ├── .gitpod-ws.code-workspace ├── .gitpod.Dockerfile ├── .gitpod.yml ├── LICENSE.md ├── README.md ├── api-testing-2022 ├── .eslintignore ├── .eslintrc ├── LICENSE ├── README.md ├── cypress.config.js ├── cypress │ ├── e2e │ │ └── network_requests.cy.js │ ├── fixtures │ │ └── example.json │ └── support │ │ ├── commands.js │ │ └── e2e.js ├── docs │ ├── API.md │ ├── ATOMIC-TESTS.md │ ├── CICD.md │ └── CONCLUSIONS.md ├── graphics │ ├── bye.gif │ ├── component-diagram.jpeg │ ├── me-and-mia.jpg │ └── secrets.png ├── package-lock.json └── package.json ├── atomic-testing ├── README.md └── package.json ├── auth-api-tutorial ├── .gitignore ├── cypress.json ├── cypress │ ├── fixtures │ │ └── example.json │ ├── plugins │ │ └── index.js │ └── support │ │ ├── commands.js │ │ └── index.js ├── dataValidation.js ├── index.js ├── model │ └── Users.js ├── package.json └── routes │ ├── auth.js │ ├── posts.js │ └── verifyToken.js ├── birthday-reminder ├── README.md ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ ├── me-and-mia.jpeg │ └── robots.txt ├── src │ ├── App.js │ ├── List.js │ ├── data.js │ ├── index.css │ └── index.js ├── test │ └── specs │ │ └── visual.spec.js └── wdio.conf.js ├── blockchain-app ├── README.md ├── client │ ├── .eslintignore │ ├── .eslintrc │ ├── .gitignore │ ├── .happo.js │ ├── .prettierrc │ ├── cypress.json │ ├── cypress │ │ ├── fixtures │ │ │ └── example.json │ │ ├── integration │ │ │ └── visual │ │ │ │ └── visual.spec.js │ │ ├── plugins │ │ │ └── index.js │ │ └── support │ │ │ ├── commands.js │ │ │ └── index.js │ ├── images │ │ ├── animated.svg │ │ ├── hello.svg │ │ └── logo.png │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── src │ │ ├── App.jsx │ │ ├── __tests__ │ │ │ └── Welcome.test.js │ │ ├── components │ │ │ ├── Footer.jsx │ │ │ ├── Loader.jsx │ │ │ ├── Navbar.jsx │ │ │ ├── Services.jsx │ │ │ ├── Transactions.jsx │ │ │ ├── Welcome.jsx │ │ │ └── index.js │ │ ├── context │ │ │ └── TransactionContext.jsx │ │ ├── favicon.svg │ │ ├── hooks │ │ │ └── useFetch.jsx │ │ ├── index.css │ │ ├── logo.svg │ │ ├── main.jsx │ │ └── utils │ │ │ ├── Transactions.json │ │ │ ├── constants.js │ │ │ ├── dummyData.js │ │ │ └── shortenAddress.js │ ├── tailwind.config.js │ └── vite.config.js └── smart_contract │ ├── .gitignore │ ├── README.md │ ├── contracts │ └── Transactions.sol │ ├── hardhat.config.js │ ├── package.json │ ├── scripts │ └── deploy.js │ └── test │ └── sample-test.js ├── cy-10 ├── .gitignore ├── cypress.config.js ├── cypress │ ├── component │ │ ├── Stepper.cy.jsx │ │ └── Stepper.jsx │ ├── fixtures │ │ └── example.json │ └── support │ │ ├── commands.js │ │ ├── component-index.html │ │ ├── component.js │ │ └── e2e.js ├── index.html ├── package-lock.json ├── package.json ├── src │ ├── App.css │ ├── App.jsx │ ├── favicon.svg │ ├── index.css │ ├── logo.svg │ └── main.jsx └── vite.config.js ├── dev-portfolio ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .happo.js ├── .prettierignore ├── .prettierrc ├── README.md ├── cypress.config.js ├── cypress │ ├── e2e │ │ ├── SocialDetails.cy.js │ │ ├── homepage.cy.js │ │ └── visual.cy.js │ ├── fixtures │ │ └── example.json │ ├── plugins │ │ └── index.js │ └── support │ │ ├── commands.js │ │ └── e2e.js ├── jest.config.js ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── images │ │ ├── birthday-reminder.webp │ │ ├── comparison-settings.webp │ │ ├── github-users-search.webp │ │ ├── portfolio.webp │ │ ├── sauce-bindings.webp │ │ ├── tic-tac-toe.webp │ │ └── ultimateqa.webp │ └── vercel.svg └── src │ ├── components │ ├── Acomplishments │ │ ├── Acomplishments.js │ │ └── AcomplishmentsStyles.js │ ├── BackgroundAnimation │ │ └── BackgroundAnimation.js │ ├── Footer │ │ ├── Footer.js │ │ └── FooterStyles.js │ ├── Header │ │ ├── Header.js │ │ ├── HeaderStyles.js │ │ └── SocialDetails.js │ ├── Hero │ │ ├── Hero.js │ │ └── HeroStyles.js │ ├── NavDropDown │ │ ├── NavDropDown.js │ │ └── index.js │ ├── Projects │ │ ├── Projects.js │ │ └── ProjectsStyles.js │ ├── Technologies │ │ ├── Technologies.js │ │ └── TechnologiesStyles.js │ └── TimeLine │ │ ├── TimeLine.js │ │ └── TimeLineStyles.js │ ├── constants │ ├── constants.js │ ├── projects.js │ ├── projects.test.js │ ├── socialUrls.js │ └── timelineData.js │ ├── layout │ ├── Layout.js │ └── LayoutStyles.js │ ├── pages │ ├── _app.js │ ├── _document.js │ ├── api │ │ └── hello.js │ └── index.js │ ├── styles │ ├── GlobalComponents │ │ ├── Button.js │ │ └── index.js │ ├── globals.js │ └── theme.js │ └── themes │ └── default.js ├── github-user-search ├── .vscode │ └── settings.json ├── README.md ├── end │ ├── cypress.json │ ├── cypress │ │ ├── fixtures │ │ │ └── example.json │ │ ├── integration │ │ │ ├── Search.spec.js │ │ │ └── error.spec.js │ │ ├── plugins │ │ │ └── index.js │ │ └── support │ │ │ ├── commands.js │ │ │ └── index.js │ ├── package.json │ ├── public │ │ ├── _redirects │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ ├── src │ │ ├── App.js │ │ ├── App.test.js │ │ ├── components │ │ │ ├── Card.js │ │ │ ├── Followers.js │ │ │ ├── Info.js │ │ │ ├── Navbar.js │ │ │ ├── Repos.js │ │ │ ├── Search.js │ │ │ ├── User.js │ │ │ └── index.js │ │ ├── context │ │ │ ├── context.js │ │ │ └── mockData.js │ │ │ │ ├── mockFollowers.js │ │ │ │ ├── mockRepos.js │ │ │ │ └── mockUser.js │ │ ├── images │ │ │ ├── login-img.svg │ │ │ └── preloader.gif │ │ ├── index.css │ │ ├── index.js │ │ ├── pages │ │ │ ├── AuthWrapper.js │ │ │ ├── Dashboard.js │ │ │ ├── Error.js │ │ │ ├── Login.js │ │ │ ├── PrivateRoute.js │ │ │ └── index.js │ │ ├── serviceWorker.js │ │ └── setupTests.js │ ├── test │ │ └── specs │ │ │ └── visual.spec.js │ ├── wdio.conf.js │ └── wdio.functional.conf.js └── start │ ├── package.json │ ├── public │ ├── _redirects │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt │ ├── src │ ├── App.js │ ├── App.test.js │ ├── components │ │ ├── Card.js │ │ ├── Followers.js │ │ ├── Info.js │ │ ├── Navbar.js │ │ ├── Repos.js │ │ ├── Search.js │ │ ├── User.js │ │ └── index.js │ ├── context │ │ ├── context.js │ │ └── mockData.js │ │ │ ├── mockFollowers.js │ │ │ ├── mockRepos.js │ │ │ └── mockUser.js │ ├── images │ │ ├── login-img.svg │ │ └── preloader.gif │ ├── index.css │ ├── index.js │ ├── pages │ │ ├── AuthWrapper.js │ │ ├── Dashboard.js │ │ ├── Error.js │ │ ├── Login.js │ │ ├── PrivateRoute.js │ │ └── index.js │ ├── serviceWorker.js │ └── setupTests.js │ ├── test │ └── specs │ │ ├── functional.spec.js │ │ └── visual.spec.js │ ├── wdio.conf.js │ └── wdio.functional.conf.js ├── react-tutorial └── react-hooks │ └── hooks-11-http-reducer │ └── .gitignore ├── storybook ├── README.md └── solution │ └── taskbox │ ├── .gitignore │ ├── .storybook │ ├── main.js │ └── preview.js │ ├── LICENSE │ ├── README.md │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt │ ├── screener.config.js │ ├── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── assets │ │ ├── font │ │ │ ├── OpenSans-Light-webfont.eot │ │ │ ├── OpenSans-Light-webfont.svg │ │ │ ├── OpenSans-Light-webfont.ttf │ │ │ ├── OpenSans-Light-webfont.woff │ │ │ ├── OpenSans-Regular-webfont.eot │ │ │ ├── OpenSans-Regular-webfont.svg │ │ │ ├── OpenSans-Regular-webfont.ttf │ │ │ └── OpenSans-Regular-webfont.woff │ │ └── icon │ │ │ ├── percolate.eot │ │ │ ├── percolate.svg │ │ │ ├── percolate.ttf │ │ │ ├── percolate.woff │ │ │ ├── todos.eot │ │ │ ├── todos.svg │ │ │ ├── todos.ttf │ │ │ └── todos.woff │ ├── components │ │ ├── Task.js │ │ ├── Task.stories.js │ │ ├── TaskList.js │ │ └── TaskList.stories.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── reportWebVitals.js │ ├── setupTests.js │ └── stories │ │ ├── Button.js │ │ ├── Button.stories.js │ │ ├── Header.js │ │ ├── Header.stories.js │ │ ├── Introduction.stories.mdx │ │ ├── Page.js │ │ ├── Page.stories.js │ │ ├── assets │ │ ├── code-brackets.svg │ │ ├── colors.svg │ │ ├── comments.svg │ │ ├── direction.svg │ │ ├── flow.svg │ │ ├── plugin.svg │ │ ├── repo.svg │ │ └── stackalt.svg │ │ ├── button.css │ │ ├── header.css │ │ └── page.css │ └── yarn.lock ├── test-optimization ├── .gitignore ├── package-lock.json ├── package.json ├── playwright.config.ts ├── tests-examples │ └── demo-todo-app.spec.ts └── tests │ └── example.spec.ts ├── testing-js ├── cypress │ └── my-app │ │ ├── .gitignore │ │ ├── README.md │ │ ├── cypress │ │ └── integration │ │ │ └── e2e │ │ │ └── cypress.demo.spec.js │ │ ├── package.json │ │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── App.test.js │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ ├── mia-dev.jpg │ │ ├── serviceWorker.js │ │ └── setupTests.js ├── frontend-performance │ ├── LICENSE.md │ ├── README.md │ └── package.json ├── jest-testing │ ├── __tests__ │ │ ├── math.spec.js │ │ ├── mocking.spec.js │ │ └── super-heros.spec.js │ ├── classes │ │ ├── math.js │ │ ├── super-heros.js │ │ └── users.js │ ├── package-lock.json │ └── package.json ├── linting │ ├── .eslintrc.js │ ├── package-lock.json │ ├── package.json │ └── src │ │ └── bad-code.js └── react-components │ └── test-app │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt │ ├── src │ ├── App.css │ ├── App.js │ ├── Button.test.js │ ├── Checkbox.test.js │ ├── __tests__ │ │ ├── hello.test.js │ │ ├── react-dom.js │ │ └── toggle.tests.js │ ├── components │ │ ├── class-component │ │ │ └── Button.js │ │ └── function-component │ │ │ ├── Button.js │ │ │ ├── Hello.js │ │ │ ├── Toggle.js │ │ │ └── favorite-number.js │ ├── index.css │ ├── index.js │ ├── reportWebVitals.js │ └── setupTests.js │ └── yarn.lock └── web-testing-2022 ├── .eslintignore ├── .eslintrc ├── LICENSE ├── README.md ├── cypress.config.js ├── cypress ├── e2e │ ├── exercise.cy.js │ ├── sanity.cy.js │ └── solution.cy.js ├── fixtures │ └── example.json └── support │ ├── commands.js │ ├── e2e.js │ └── e2eConstants.js ├── docs ├── ATOMIC-TESTS.md ├── CICD.md ├── COMPONENT-TESTS.md ├── CONCLUSIONS.md ├── E2E-TESTS.md ├── PERF.md ├── TEST-COVERAGE.md ├── VISUAL.md └── visual-testing.pdf ├── graphics ├── bye.gif ├── component-diagram.jpeg ├── me-and-mia.jpg └── secrets.png ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt └── src ├── App.css ├── App.js ├── App.test.js ├── index.css ├── index.js ├── logo.svg ├── reportWebVitals.js └── setupTests.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: nadvolod 2 | -------------------------------------------------------------------------------- /.github/workflows/blockchain.app.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Blockchain App CI 5 | 6 | on: 7 | push: 8 | branches: [ main ] 9 | pull_request: 10 | branches: [ main ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | strategy: 17 | matrix: 18 | node-version: [14.x, 16.x] 19 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 20 | 21 | defaults: 22 | run: 23 | working-directory: ./blockchain-app/client/ 24 | steps: 25 | - uses: actions/checkout@v2 26 | - name: Use Node.js ${{ matrix.node-version }} 27 | uses: actions/setup-node@v2 28 | with: 29 | node-version: ${{ matrix.node-version }} 30 | cache: 'npm' 31 | cache-dependency-path: blockchain-app/client/package-lock.json 32 | - name: Install dependencies 📦 33 | #Using npm ci is generally faster than running npm install 34 | run: | 35 | npm ci 36 | - name: Build the app for prod 🏗 37 | run: | 38 | npm run build 39 | - name: Start the app 📤 40 | #Vite has a different command for deploy than vanilla react 41 | run: | 42 | npm run preview & 43 | npx wait-on --timeout 60000 44 | - name: Run visual tests 👁 45 | run: | 46 | npm run cy:visual 47 | -------------------------------------------------------------------------------- /.github/workflows/master_nikolay-react.yml: -------------------------------------------------------------------------------- 1 | # Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy 2 | # More GitHub Actions for Azure: https://github.com/Azure/actions 3 | 4 | name: Build and deploy Node.js app to Azure Web App - nikolay-react 5 | 6 | on: 7 | push: 8 | branches: 9 | - none 10 | 11 | jobs: 12 | build-and-deploy: 13 | runs-on: windows-latest 14 | 15 | steps: 16 | - uses: actions/checkout@master 17 | 18 | - name: Set up Node.js version 19 | uses: actions/setup-node@v1 20 | with: 21 | node-version: '12.13.0' 22 | 23 | - name: npm install, build, and test 24 | run: | 25 | npm install 26 | npm run build --if-present 27 | npm run test --if-present 28 | 29 | - name: 'Deploy to Azure Web App' 30 | uses: azure/webapps-deploy@v2 31 | with: 32 | app-name: 'nikolay-react' 33 | slot-name: 'production' 34 | publish-profile: ${{ secrets.AzureAppService_PublishProfile_beb0a9f8c66246119ef00e3abd37b968 }} 35 | package: . 36 | -------------------------------------------------------------------------------- /.gitpod-ws.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folder": [ 3 | { "path": "web-testing-2022" } 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.gitpod.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gitpod/workspace-full-vnc:latest 2 | 3 | ENV CYPRESS_CACHE_FOLDER=/workspace/.cypress-cache 4 | 5 | # Install Cypress dependencies. 6 | RUN sudo apt-get update \ 7 | && sudo DEBIAN_FRONTEND=noninteractive apt-get install -y \ 8 | libgtk2.0-0 \ 9 | libgtk-3-0 \ 10 | libnotify-dev \ 11 | libgconf-2-4 \ 12 | libnss3 \ 13 | libxss1 \ 14 | libasound2 \ 15 | libxtst6 \ 16 | xauth \ 17 | xvfb \ 18 | && sudo rm -rf /var/lib/apt/lists/* 19 | 20 | # Install Firefox 21 | 22 | RUN sudo apt-get update -q \ 23 | && sudo apt-get install -yq \ 24 | firefox \ 25 | && sudo rm -rf /var/lib/apt/lists/* 26 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | image: 2 | file: .gitpod.Dockerfile 3 | workspaceLocation: gitpod/gitpod-ws.code-workspace 4 | 5 | ports: 6 | - port: 3000 7 | onOpen: open-preview 8 | visibility: public 9 | name: Website 10 | description: Preview of Website 11 | - port: 5900 12 | onOpen: ignore 13 | name: VNC 14 | description: VNC Server 15 | - port: 6080 16 | onOpen: open-preview 17 | visibility: public 18 | name: VNC 19 | description: VNC Viewer 20 | 21 | # List the start up tasks. You can start them in parallel in multiple terminals. See https://www.gitpod.io/docs/configure/workspaces/tasks/ 22 | tasks: 23 | - init: npm install 24 | command: npm run start 25 | name: Start 26 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Nikolay Advolodkin www.ultimateqa.com 2 | 3 | Permission to use, copy, modify, and distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # So much JS Code! 2 | 3 | [![My Dev Portfolio](https://api.netlify.com/api/v1/badges/ae0fc361-7a70-4c05-967e-ff1117244446/deploy-status)](https://app.netlify.com/sites/boring-goldwasser-4ff12b/deploys) 4 | 5 | Tons of JavaScript concepts in a single repo! 6 | 7 | **🥗 Like my content? [Buy me a salad](https://www.buymeacoffee.com/nikolaya)** 8 | 9 | **📬[Get notified of latest projects and JS testing tips](https://ultimateqa.ck.page/js-testing-tips)** 10 | 11 | ## TOC 12 | 13 | - [API Testing w/ Cypress](https://github.com/nadvolod/cypress-api-testing-22) `cypress10` 14 | - [Birthday reminder web app, ReactJS, Screener, WebdriverIO, Sauce Labs](./birthday-reminder) 15 | - [Personal dev portfolio](./dev-portfolio) `nextjs` `netlify` 16 | - [Test React components using component tests](./testing-js/react-components/test-app/) 17 | - [Github User Search React App](./github-user-search/) 18 | - [Modern Web 3.0 Blockchain APp]() 19 | - [Web testing in 2023](./web-testing-2022) `cypress10` `happoio` `cicd` 20 | 21 | ### Concepts 22 | 23 | - [WebdriverIO w/ Screener.io on localhost](./birthday-reminder/wdio.conf.js) 24 | - [Sauce connect Proxy] be sure to run with a higher [ulimit](https://support.saucelabs.com/hc/en-us/articles/115005571668) 25 | -------------------------------------------------------------------------------- /api-testing-2022/.eslintignore: -------------------------------------------------------------------------------- 1 | .tmp/ 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /api-testing-2022/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 saucelabs-training 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /api-testing-2022/README.md: -------------------------------------------------------------------------------- 1 | # api testing with javascript workshop 2 | 3 | [#testing4good](https://twitter.com/hashtag/Testing4Good) 4 | 5 | In this workshop you will learn how to do automated API testing with Cypress 10. 6 | 7 | ## [Moved to this repo](https://github.com/nadvolod/cypress-api-testing-22) 8 | -------------------------------------------------------------------------------- /api-testing-2022/cypress.config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require("cypress"); 2 | 3 | module.exports = defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /api-testing-2022/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /api-testing-2022/cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add('login', (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This will overwrite an existing command -- 25 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) -------------------------------------------------------------------------------- /api-testing-2022/cypress/support/e2e.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/e2e.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') -------------------------------------------------------------------------------- /api-testing-2022/docs/CICD.md: -------------------------------------------------------------------------------- 1 | # CICD 2 | 3 | ## 🏋️‍♀️Let's add this code to our CI system. 4 | 5 | 1. In the repository's Github, go to `Actions` tab 6 | 2. Configure `Node.js` 7 | 3. Keep the name of the file and on line 4, or change, up to you 8 | 4. Copy + Paste the code from [comprehensive.yml](../.github/workflows/comprehensive.yml) 9 | 10 | 5. Add New repository secrets for the repo 11 | 12 | 6. Commit the new file 13 | 7. `git push` all of your changes to your repo 14 | 15 | ## 🧪Current Test Coverage 16 | 17 | [Look here](TEST-COVERAGE.md) 18 | 19 | ## 📝Summary 20 | 21 | ✅ We can use Github Workflows for free and easy continuous integration pipelines 22 | 23 | ## ⏭️[Conclusions](./CONCLUSIONS.md) 24 | -------------------------------------------------------------------------------- /api-testing-2022/docs/CONCLUSIONS.md: -------------------------------------------------------------------------------- 1 | # conclusions 2 | 3 | wait 4 | 5 | ## Thanks so much for your time and generosity 🙌👏 6 | 7 | Ecosia 8 | 9 | 1. We raised... 10 | 2. Please leave some quick and anonymous [feedback on this workshop](https://docs.google.com/forms/d/e/1FAIpQLSes4ft4_0JGFQnMB5Tj2L6J3-IWLX2jqyRIc7ejrDlLiCfIyg/viewform?usp=sf_link) 11 | 3. 📫 Follow me as I will do many more Testing for Good events 12 | 13 | - [JS Testing Newsletter](https://ultimateqa.ck.page/js-testing-tips) 14 | - [Youtube](https://youtube.com/ultimateqa) 15 | - [LinkedIn](https://www.linkedin.com/in/nikolayadvolodkin/) 16 | - [Twitter](https://twitter.com/Nikolay_A00) 17 | 18 | 4. Give this repo a ⭐ 19 | 20 | ## 💃Let's pick a winner for a backpack! 21 | 22 | --- 23 | 24 | ## Bonus resources 25 | 26 | - [How to code and test an authentication API tutorial](https://youtu.be/klIAT82UtVs) 27 | - [Build and test a Github users search app tutorial](https://youtu.be/EvZ6pjgYA38) 28 | - [What is REST blog post](https://restfulapi.net/) 29 | - [API testing in JavaScript tutorial](https://testautomationu.applitools.com/javascript-api-testing/) 30 | -------------------------------------------------------------------------------- /api-testing-2022/graphics/bye.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/api-testing-2022/graphics/bye.gif -------------------------------------------------------------------------------- /api-testing-2022/graphics/component-diagram.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/api-testing-2022/graphics/component-diagram.jpeg -------------------------------------------------------------------------------- /api-testing-2022/graphics/me-and-mia.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/api-testing-2022/graphics/me-and-mia.jpg -------------------------------------------------------------------------------- /api-testing-2022/graphics/secrets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/api-testing-2022/graphics/secrets.png -------------------------------------------------------------------------------- /api-testing-2022/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "api-testing-2022", 3 | "version": "1.0.0", 4 | "description": "[#testing4good](https://twitter.com/hashtag/Testing4Good)", 5 | "main": "index.js", 6 | "directories": { 7 | "doc": "docs" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "keywords": [], 13 | "author": "", 14 | "license": "ISC", 15 | "devDependencies": { 16 | "cypress": "^10.4.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /atomic-testing/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "atomic-testing", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC" 12 | } 13 | -------------------------------------------------------------------------------- /auth-api-tutorial/.gitignore: -------------------------------------------------------------------------------- 1 | *.env 2 | /cypress/screenshots/ 3 | ../authentication-api/* 4 | ../cypress/* -------------------------------------------------------------------------------- /auth-api-tutorial/cypress.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /auth-api-tutorial/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } -------------------------------------------------------------------------------- /auth-api-tutorial/cypress/plugins/index.js: -------------------------------------------------------------------------------- 1 | /// 2 | // *********************************************************** 3 | // This example plugins/index.js can be used to load plugins 4 | // 5 | // You can change the location of this file or turn off loading 6 | // the plugins file with the 'pluginsFile' configuration option. 7 | // 8 | // You can read more here: 9 | // https://on.cypress.io/plugins-guide 10 | // *********************************************************** 11 | 12 | // This function is called when a project is opened or re-opened (e.g. due to 13 | // the project's config changing) 14 | 15 | /** 16 | * @type {Cypress.PluginConfig} 17 | */ 18 | module.exports = (on, config) => { 19 | // `on` is used to hook into various events Cypress emits 20 | // `config` is the resolved Cypress config 21 | } 22 | -------------------------------------------------------------------------------- /auth-api-tutorial/cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add("login", (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This will overwrite an existing command -- 25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) 26 | -------------------------------------------------------------------------------- /auth-api-tutorial/cypress/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /auth-api-tutorial/dataValidation.js: -------------------------------------------------------------------------------- 1 | const Joi = require('@hapi/joi'); 2 | 3 | class DataValidation{ 4 | static registerValidation = request => { 5 | const validationSchema = Joi.object({ 6 | name: Joi.string().min(2).required(), 7 | email: Joi.string().min(6).required().email(), 8 | password: Joi.string().min(8).required() 9 | }) 10 | return validationSchema.validate(request); 11 | } 12 | 13 | static loginValidation = request => { 14 | const validationSchema = Joi.object({ 15 | email: Joi.string().min(3).required().email(), 16 | password: Joi.string().min(8).required() 17 | }) 18 | return validationSchema.validate(request); 19 | } 20 | } 21 | 22 | module.exports = {DataValidation}; 23 | -------------------------------------------------------------------------------- /auth-api-tutorial/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | const mongoose = require('mongoose'); 4 | const dotenv = require('dotenv'); 5 | //Import routes 6 | const authRoute = require('./routes/auth'); 7 | const postRoute = require('./routes/posts'); 8 | 9 | //setup to read an env variable from a file 10 | dotenv.config(); 11 | 12 | //Connect to the DB 13 | mongoose.connect(process.env.DB_CONNECT, 14 | { useNewUrlParser: true }, 15 | ()=> console.log('Connected to db')) 16 | 17 | //Middleware 18 | app.use(express.json()); 19 | //Route middleware 20 | app.use('/api/user', authRoute); 21 | app.use('/api/posts', postRoute); 22 | 23 | app.listen(3000, ()=> console.log('Server is running')); -------------------------------------------------------------------------------- /auth-api-tutorial/model/Users.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const userSchema = new mongoose.Schema({ 4 | name:{ 5 | type: String, 6 | required: true, 7 | min: 6, 8 | max: 255 9 | }, 10 | email:{ 11 | type: String, 12 | required: true, 13 | max: 255, 14 | min: 6 15 | }, 16 | password:{ 17 | type: String, 18 | required: true, 19 | max: 1024, 20 | min: 6 21 | }, 22 | date: { 23 | type: Date, 24 | default: Date.now 25 | } 26 | }) 27 | 28 | module.exports = mongoose.model('User', userSchema); -------------------------------------------------------------------------------- /auth-api-tutorial/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "auth-api-tutorial", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon index.js", 8 | "test": "cypress run" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "@hapi/joi": "^17.1.1", 15 | "bcryptjs": "^2.4.3", 16 | "dotenv": "^8.2.0", 17 | "express": "^4.17.1", 18 | "jsonwebtoken": "^8.5.1", 19 | "mongoose": "^5.11.8" 20 | }, 21 | "devDependencies": { 22 | "cypress": "^6.1.0", 23 | "guid": "0.0.12", 24 | "nodemon": "^2.0.6" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /auth-api-tutorial/routes/posts.js: -------------------------------------------------------------------------------- 1 | const router = require('express').Router(); 2 | const verify = require('./verifyToken'); 3 | 4 | // http://localhost:3001/api/posts 5 | //By adding the verify, we made this route private that requires a token 6 | router.get('/', verify, async(req,res) => { 7 | res.json({ 8 | posts:{ 9 | title: 'my first post', 10 | description: 'data you shouldnt be able to access' 11 | } 12 | }) 13 | }); 14 | 15 | 16 | module.exports = router; -------------------------------------------------------------------------------- /auth-api-tutorial/routes/verifyToken.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | 3 | module.exports = function (request, response, next) { 4 | //checking the header for the auth-token value 5 | const token = request.header('auth-token'); 6 | //if the token doesn't exist then Access Denied 7 | if(!token) return response.status(401).send('Access Denied'); 8 | 9 | //try to validate that the token is valid 10 | try { 11 | const verified = jwt.verify(token, process.env.TOKEN_SECRET); 12 | request.user = verified; 13 | next(); 14 | }catch(err){ 15 | response.status(400).send('Invalid Token'); 16 | } 17 | } -------------------------------------------------------------------------------- /birthday-reminder/README.md: -------------------------------------------------------------------------------- 1 | # Birthday Reminder React App 2 | 3 | ## Getting started 4 | 5 | ```js 6 | cd birthday-reminder 7 | npm i && npm start 8 | //run tests 9 | npm run wdio 10 | ``` 11 | 12 | [The production version of the website](https://laughing-feynman-11feb4.netlify.app/) 13 | 14 | ## Nikolay Advolodkin 15 | 16 | me 17 | 18 | - 🔭 I’m the founder of [Ultimate QA](https://ultimateqa.com/) a Sr Solutions Architect at [Sauce Labs](https://saucelabs.com/) 19 | - 👨‍💻 I’m currently working on [Sauce Bindings](https://github.com/saucelabs/sauce_bindings) 20 | - 💬 Ask me about environmentalism, veganism, test automation, and fitness 21 | - 😄 Pronouns: he/him 22 | - ⚡ Fun fact: I'm a vegan that's super pasionate about saving the planet, saving animals, and helping underpriveleged communities 23 | - 📫 Follow me for testing and dev training 24 | - [JS Testing Newsletter](https://ultimateqa.ck.page/js-testing-tips) to learn testing like a pro 25 | - [Youtube](https://youtube.com/ultimateqa) for 🔥 training tutorials 26 | - [LinkedIn](https://www.linkedin.com/in/nikolayadvolodkin/) for 👍 conversations 27 | - [Twitter](https://twitter.com/Nikolay_A00) for awesome quotes from 🧠 people 28 | 29 | ## Idea 30 | 31 | [https://uidesigndaily.com/](https://uidesigndaily.com/posts/sketch-birthdays-list-card-widget-day-1042) 32 | -------------------------------------------------------------------------------- /birthday-reminder/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reminder", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.1", 7 | "@testing-library/react": "^12.1.2", 8 | "@testing-library/user-event": "^13.5.0", 9 | "react": "^17.0.2", 10 | "react-dom": "^17.0.2", 11 | "react-scripts": "^5.0.0" 12 | }, 13 | "scripts": { 14 | "start": "react-scripts start", 15 | "build": "CI= react-scripts build", 16 | "test": "react-scripts test", 17 | "eject": "react-scripts eject", 18 | "wdio": "wdio run wdio.conf.js" 19 | }, 20 | "eslintConfig": { 21 | "extends": "react-app" 22 | }, 23 | "browserslist": { 24 | "production": [ 25 | ">0.2%", 26 | "not dead", 27 | "not op_mini all" 28 | ], 29 | "development": [ 30 | "last 1 chrome version", 31 | "last 1 firefox version", 32 | "last 1 safari version" 33 | ] 34 | }, 35 | "devDependencies": { 36 | "@wdio/cli": "^7.16.13", 37 | "@wdio/local-runner": "^7.16.13", 38 | "@wdio/mocha-framework": "^7.16.13", 39 | "@wdio/sauce-service": "^7.16.13", 40 | "@wdio/spec-reporter": "^7.16.13", 41 | "chromedriver": "^97.0.0", 42 | "wdio-chromedriver-service": "^7.2.6", 43 | "wdio-slack-service": "^2.0.9" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /birthday-reminder/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/birthday-reminder/public/favicon.ico -------------------------------------------------------------------------------- /birthday-reminder/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/birthday-reminder/public/logo192.png -------------------------------------------------------------------------------- /birthday-reminder/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/birthday-reminder/public/logo512.png -------------------------------------------------------------------------------- /birthday-reminder/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /birthday-reminder/public/me-and-mia.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/birthday-reminder/public/me-and-mia.jpeg -------------------------------------------------------------------------------- /birthday-reminder/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /birthday-reminder/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import data from './data'; 3 | import List from './List'; 4 | function App() { 5 | const [people, setPeople] = useState(data); 6 | 7 | return ( 8 |
9 |
10 |

{people.length} birthdays

11 | 12 | 15 |
16 |
17 | ); 18 | } 19 | 20 | export default App; 21 | -------------------------------------------------------------------------------- /birthday-reminder/src/List.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const List = ({ people }) => { 4 | return ( 5 | <> 6 | {people.map((person) => { 7 | const { id, name, age, image } = person; 8 | return ( 9 |
10 | {name} 11 |
12 |

{name}

13 |

{age} years

14 |
15 |
16 | ); 17 | })} 18 | 19 | ); 20 | }; 21 | 22 | export default List; 23 | -------------------------------------------------------------------------------- /birthday-reminder/src/data.js: -------------------------------------------------------------------------------- 1 | // This is the same as what we have in our App.js 2 | // except instead of creating a function and exporting a named 3 | // function, we can just export default 4 | export default [ 5 | { 6 | id: 1, 7 | name: 'Bertie Yates', 8 | age: 29, 9 | image: 10 | 'https://res.cloudinary.com/diqqf3eq2/image/upload/v1595959131/person-2_ipcjws.jpg', 11 | }, 12 | { 13 | id: 2, 14 | name: 'Hester Hogan', 15 | age: 32, 16 | image: 17 | 'https://res.cloudinary.com/diqqf3eq2/image/upload/v1595959131/person-3_rxtqvi.jpg', 18 | }, 19 | { 20 | id: 3, 21 | name: 'Larry Little', 22 | age: 36, 23 | image: 24 | 'https://res.cloudinary.com/diqqf3eq2/image/upload/v1586883423/person-4_t9nxjt.jpg', 25 | }, 26 | { 27 | id: 4, 28 | name: 'Sean Walsh', 29 | age: 34, 30 | image: 31 | 'https://res.cloudinary.com/diqqf3eq2/image/upload/v1586883417/person-3_ipa0mj.jpg', 32 | }, 33 | { 34 | id: 5, 35 | name: 'Lola Gardner', 36 | age: 29, 37 | image: 38 | 'https://res.cloudinary.com/diqqf3eq2/image/upload/v1586883334/person-1_rfzshl.jpg', 39 | }, 40 | ]; 41 | -------------------------------------------------------------------------------- /birthday-reminder/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ); 12 | -------------------------------------------------------------------------------- /birthday-reminder/test/specs/visual.spec.js: -------------------------------------------------------------------------------- 1 | describe('My birthday reminders app', () => { 2 | it('should look correctly', async () => { 3 | await browser.url(``); 4 | await browser.execute('/*@visual.init*/', 'Birthday Reminder App'); 5 | await browser.execute('/*@visual.snapshot*/', 'Default State'); 6 | 7 | await $('[data-testid="clear"]').click(); 8 | await browser.execute('/*@visual.snapshot*/', 'Clear State'); 9 | 10 | const result = await browser.execute('/*@visual.end*/'); 11 | expect(result.message).toBeNull(); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /birthday-reminder/wdio.conf.js: -------------------------------------------------------------------------------- 1 | const visualOptions = { 2 | apiKey: process.env.SCREENER_API_KEY, 3 | projectName: 'ctm2022', 4 | }; 5 | const sauceOptions = { 6 | username: process.env.SAUCE_USERNAME, 7 | accesskey: process.env.SAUCE_ACCESS_KEY, 8 | build: `Visual Tests-${new Date().getTime()}`, 9 | //enable HAR files 10 | extendedDebugging: true, 11 | capturePerformance: true, 12 | }; 13 | exports.config = { 14 | region: process.env.REGION || 'us', 15 | services: [ 16 | [ 17 | 'sauce', 18 | { 19 | sauceConnect: true, 20 | }, 21 | ], 22 | ], 23 | specs: ['./test/specs/**/*.js'], 24 | // Patterns to exclude. 25 | exclude: [ 26 | // 'path/to/excluded/files' 27 | ], 28 | maxInstances: 10, 29 | hostname: 'hub.screener.io', 30 | port: 443, 31 | protocol: 'https', 32 | path: '/wd/hub', 33 | capabilities: [ 34 | //Desktop A 28%: https://www.w3schools.com/browsers/browsers_display.asp 35 | { 36 | browserName: 'chrome', 37 | platformName: 'windows 10', 38 | browserVersion: 'latest', 39 | 'sauce:options': { 40 | ...sauceOptions, 41 | }, 42 | 'sauce:visual': { 43 | ...visualOptions, 44 | viewportSize: '1366x768', 45 | }, 46 | }, 47 | ], 48 | // 49 | // =================== 50 | // Test Configurations 51 | // =================== 52 | // Level of logging verbosity: trace | debug | info | warn | error | silent 53 | logLevel: 'error', 54 | bail: 0, 55 | baseUrl: 'http://localhost:3000', 56 | waitforTimeout: 10000, 57 | connectionRetryTimeout: 120000, 58 | connectionRetryCount: 3, 59 | framework: 'mocha', 60 | reporters: ['spec'], 61 | mochaOpts: { 62 | ui: 'bdd', 63 | timeout: 180000, 64 | }, 65 | }; 66 | -------------------------------------------------------------------------------- /blockchain-app/client/.eslintignore: -------------------------------------------------------------------------------- 1 | .tmp/ 2 | node_modules/ 3 | /build/** 4 | /coverage/** 5 | /docs/** 6 | /jsdoc/** 7 | /templates/** 8 | /tests/bench/** 9 | /tests/fixtures/** 10 | /tests/performance/** 11 | /tmp/** 12 | /tools/internal-rules/node_modules/** 13 | test.js 14 | !.eslintrc.js -------------------------------------------------------------------------------- /blockchain-app/client/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /blockchain-app/client/.happo.js: -------------------------------------------------------------------------------- 1 | const { RemoteBrowserTarget } = require('happo.io') 2 | 3 | module.exports = { 4 | apiKey: process.env.HAPPO_API_KEY, 5 | apiSecret: process.env.HAPPO_API_SECRET, 6 | 7 | targets: { 8 | 'chrome-1080p': new RemoteBrowserTarget('chrome', { 9 | viewport: '1920x1080', 10 | freezeAnimations: 'last-frame', 11 | }), 12 | // all viewports https://viewportsizer.com/devices/ 13 | // Samsung Galaxy S10+, S10 14 | 'chrome-galaxy-s10': new RemoteBrowserTarget('chrome', { 15 | viewport: '360x740', 16 | freezeAnimations: 'last-frame', 17 | }), 18 | // iphone viewports https://yesviz.com/iphones.php 19 | 'safari-1080p': new RemoteBrowserTarget('safari', { 20 | viewport: '1920x1080', 21 | freezeAnimations: 'last-frame', 22 | }), 23 | // iphone 12 pro 24 | 'safari-iphone-12-pro': new RemoteBrowserTarget('safari', { 25 | viewport: '390x844', 26 | freezeAnimations: 'last-frame', 27 | }), 28 | }, 29 | // only works on public urls 30 | // pages: [{ url: 'http://localhost:3000/', title: 'Home Page' }], 31 | } 32 | -------------------------------------------------------------------------------- /blockchain-app/client/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "semi": false, 4 | "singleQuote": true, 5 | "tabWidth": 4, 6 | "trailingComma": "es5" 7 | } -------------------------------------------------------------------------------- /blockchain-app/client/cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseUrl": "http://localhost:3000" 3 | } 4 | -------------------------------------------------------------------------------- /blockchain-app/client/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /blockchain-app/client/cypress/integration/visual/visual.spec.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe('blockchain app', () => { 4 | beforeEach(() => { 5 | cy.visit('') 6 | }) 7 | 8 | it('displays the home page correctly', () => { 9 | cy.get('body').happoScreenshot() 10 | }) 11 | 12 | it('displays correct hamburger menu', () => { 13 | cy.viewport('samsung-s10') 14 | cy.get('[data-testid="hamburger-menu"]').click() 15 | cy.get('[data-testid="mobile-menu-open"]').happoScreenshot({ 16 | component: 'hamburger-menu', 17 | targets: ['chrome-galaxy-s10'], 18 | }) 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /blockchain-app/client/cypress/plugins/index.js: -------------------------------------------------------------------------------- 1 | /// 2 | // *********************************************************** 3 | // This example plugins/index.js can be used to load plugins 4 | // 5 | // You can change the location of this file or turn off loading 6 | // the plugins file with the 'pluginsFile' configuration option. 7 | // 8 | // You can read more here: 9 | // https://on.cypress.io/plugins-guide 10 | // *********************************************************** 11 | 12 | // This function is called when a project is opened or re-opened (e.g. due to 13 | // the project's config changing) 14 | 15 | /** 16 | * @type {Cypress.PluginConfig} 17 | */ 18 | // eslint-disable-next-line no-unused-vars 19 | module.exports = (on, config) => { 20 | // `on` is used to hook into various events Cypress emits 21 | // `config` is the resolved Cypress config 22 | } 23 | const happoTask = require('happo-cypress/task') 24 | 25 | module.exports = (on) => { 26 | happoTask.register(on) 27 | } 28 | -------------------------------------------------------------------------------- /blockchain-app/client/cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add('login', (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This will overwrite an existing command -- 25 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) 26 | import 'happo-cypress' 27 | -------------------------------------------------------------------------------- /blockchain-app/client/cypress/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /blockchain-app/client/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/blockchain-app/client/images/logo.png -------------------------------------------------------------------------------- /blockchain-app/client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /blockchain-app/client/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /blockchain-app/client/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { Navbar, Welcome, Footer, Services, Transactions } from './components' 2 | 3 | const App = () => ( 4 |
5 |
6 | 7 | 8 |
9 | 10 | 11 |
12 |
13 | ) 14 | 15 | export default App 16 | -------------------------------------------------------------------------------- /blockchain-app/client/src/__tests__/Welcome.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render, fireEvent, waitFor, screen } from '@testing-library/react' 3 | import Welcome from '../components/Welcome' 4 | 5 | describe('Welcome', () => { 6 | test('renders', () => { 7 | render() 8 | screen.debug() 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /blockchain-app/client/src/components/Loader.jsx: -------------------------------------------------------------------------------- 1 | const Loader = () => ( 2 |
3 |
4 |
5 | ); 6 | 7 | export default Loader; 8 | -------------------------------------------------------------------------------- /blockchain-app/client/src/components/index.js: -------------------------------------------------------------------------------- 1 | export { default as Loader } from "./Loader"; 2 | export { default as Navbar } from "./Navbar"; 3 | export { default as Welcome } from "./Welcome"; 4 | export { default as Footer } from "./Footer"; 5 | export { default as Services } from "./Services"; 6 | export { default as Transactions } from "./Transactions"; 7 | -------------------------------------------------------------------------------- /blockchain-app/client/src/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /blockchain-app/client/src/hooks/useFetch.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react' 2 | 3 | const APIKEY = import.meta.env.VITE_GIPHY_API 4 | 5 | const useFetch = ({ keyword }) => { 6 | const [gifUrl, setGifUrl] = useState('') 7 | 8 | const fetchGifs = async () => { 9 | try { 10 | const response = await fetch( 11 | `https://api.giphy.com/v1/gifs/search?api_key=${APIKEY}&q=${keyword 12 | .split(' ') 13 | .join('')}&limit=1` 14 | ) 15 | const { data } = await response.json() 16 | 17 | setGifUrl(data[0]?.images?.downsized_medium.url) 18 | } catch (error) { 19 | setGifUrl( 20 | 'https://metro.co.uk/wp-content/uploads/2015/05/pokemon_crying.gif?quality=90&strip=all&zoom=1&resize=500%2C284' 21 | ) 22 | } 23 | } 24 | 25 | useEffect(() => { 26 | if (keyword) fetchGifs() 27 | }, [keyword]) 28 | 29 | return gifUrl 30 | } 31 | 32 | export default useFetch 33 | -------------------------------------------------------------------------------- /blockchain-app/client/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | 4 | import App from './App' 5 | import { TransactionsProvider } from './context/TransactionContext' 6 | import './index.css' 7 | 8 | ReactDOM.render( 9 | 10 | 11 | , 12 | document.getElementById('root') 13 | ) 14 | -------------------------------------------------------------------------------- /blockchain-app/client/src/utils/constants.js: -------------------------------------------------------------------------------- 1 | import abi from './Transactions.json' 2 | 3 | export const contractABI = abi.abi 4 | // this is where hardhat deployed the contract. Came from the console 5 | export const contractAddress = '0x904F6eD53Bb20379136481caAb9393aE131a049e' 6 | -------------------------------------------------------------------------------- /blockchain-app/client/src/utils/shortenAddress.js: -------------------------------------------------------------------------------- 1 | export const shortenAddress = (address) => `${address.slice(0, 5)}...${address.slice(address.length - 4)}`; 2 | -------------------------------------------------------------------------------- /blockchain-app/client/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | purge: ['./src/**/*.{js,jsx,ts,tsx}', './public/index.html'], 3 | mode: 'jit', 4 | darkMode: false, // or 'media' or 'class' 5 | theme: { 6 | fontFamily: { 7 | display: ['Open Sans', 'sans-serif'], 8 | body: ['Open Sans', 'sans-serif'], 9 | }, 10 | extend: { 11 | // add a custom screen mf = medium full 12 | screens: { 13 | mf: '990px', 14 | }, 15 | keyframes: { 16 | 'slide-in': { 17 | '0%': { 18 | '-webkit-transform': 'translateX(120%)', 19 | transform: 'translateX(120%)', 20 | }, 21 | '100%': { 22 | '-webkit-transform': 'translateX(0%)', 23 | transform: 'translateX(0%)', 24 | }, 25 | }, 26 | }, 27 | animation: { 28 | 'slide-in': 'slide-in 0.5s ease-out', 29 | }, 30 | }, 31 | }, 32 | variants: { 33 | extend: {}, 34 | }, 35 | plugins: [require('@tailwindcss/forms')], 36 | } 37 | -------------------------------------------------------------------------------- /blockchain-app/client/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /blockchain-app/smart_contract/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | coverage 4 | coverage.json 5 | typechain 6 | 7 | #Hardhat files 8 | cache 9 | artifacts 10 | -------------------------------------------------------------------------------- /blockchain-app/smart_contract/README.md: -------------------------------------------------------------------------------- 1 | # Basic Sample Hardhat Project 2 | 3 | This project demonstrates a basic Hardhat use case. It comes with a sample contract, a test for that contract, a sample script that deploys that contract, and an example of a task implementation, which simply lists the available accounts. 4 | 5 | Try running some of the following tasks: 6 | 7 | ```shell 8 | npx hardhat accounts 9 | npx hardhat compile 10 | npx hardhat clean 11 | npx hardhat test 12 | npx hardhat node 13 | node scripts/sample-script.js 14 | npx hardhat help 15 | ``` 16 | -------------------------------------------------------------------------------- /blockchain-app/smart_contract/contracts/Transactions.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.0; 3 | 4 | contract Transactions { 5 | uint256 transactionCount; 6 | 7 | event Transfer(address from, address receiver, uint256 amount, string message, uint256 timestamp, string keyword); 8 | // array of objects 9 | struct TransferStruct{ 10 | address sender; 11 | address receiver; 12 | uint amount; 13 | string message; 14 | uint256 timestamp; 15 | string keyword; 16 | } 17 | 18 | TransferStruct[] transactions; 19 | 20 | function addToBlockchain(address payable receiver, uint amount, string memory message, string memory keyword) public { 21 | transactionCount+=1; 22 | // what does push do? 23 | transactions.push(TransferStruct(msg.sender, receiver, amount, message, block.timestamp, keyword)); 24 | emit Transfer(msg.sender, receiver, amount, message, block.timestamp, keyword); 25 | } 26 | function getAllTransactions() public view returns (TransferStruct[] memory) { 27 | return transactions; 28 | } 29 | function getTransactionCount() public view returns (uint256){ 30 | return transactionCount; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /blockchain-app/smart_contract/hardhat.config.js: -------------------------------------------------------------------------------- 1 | //plugin to build smart contract tests 2 | require('@nomiclabs/hardhat-waffle'); 3 | 4 | module.exports = { 5 | solidity: '0.8.0', 6 | networks: { 7 | ropsten: { 8 | url: process.env.ALCHEMY_ETH_ROPSTEN_URL, 9 | // get private key from metamask 10 | accounts: [process.env.TRANSACTIONS_PRIVATE_KEY], 11 | }, 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /blockchain-app/smart_contract/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "smart_contract", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "@nomiclabs/hardhat-ethers": "^2.0.5", 14 | "@nomiclabs/hardhat-waffle": "^2.0.2", 15 | "chai": "^4.3.6", 16 | "ethereum-waffle": "^3.4.0", 17 | "ethers": "^5.5.4", 18 | "hardhat": "^2.8.4" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /blockchain-app/smart_contract/scripts/deploy.js: -------------------------------------------------------------------------------- 1 | // We require the Hardhat Runtime Environment explicitly here. This is optional 2 | // but useful for running the script in a standalone fashion through `node 12 | 13 | 14 | -------------------------------------------------------------------------------- /cy-10/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cy-10", 3 | "private": true, 4 | "version": "0.0.0", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vite build", 8 | "preview": "vite preview" 9 | }, 10 | "dependencies": { 11 | "react": "^18.0.0", 12 | "react-dom": "^18.0.0" 13 | }, 14 | "devDependencies": { 15 | "@types/react": "^18.0.0", 16 | "@types/react-dom": "^18.0.0", 17 | "@vitejs/plugin-react": "^1.3.0", 18 | "cypress": "^10.0.3", 19 | "vite": "^2.9.9" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /cy-10/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | 40 | button { 41 | font-size: calc(10px + 2vmin); 42 | } 43 | -------------------------------------------------------------------------------- /cy-10/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import logo from './logo.svg' 3 | import './App.css' 4 | 5 | function App() { 6 | const [count, setCount] = useState(0) 7 | 8 | return ( 9 |
10 |
11 | logo 12 |

Hello Vite + React!

13 |

14 | 17 |

18 |

19 | Edit App.jsx and save to test HMR updates. 20 |

21 |

22 | 28 | Learn React 29 | 30 | {' | '} 31 | 37 | Vite Docs 38 | 39 |

40 |
41 |
42 | ) 43 | } 44 | 45 | export default App 46 | -------------------------------------------------------------------------------- /cy-10/src/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /cy-10/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /cy-10/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | 10 | ) 11 | -------------------------------------------------------------------------------- /cy-10/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /dev-portfolio/.eslintignore: -------------------------------------------------------------------------------- 1 | .tmp/ 2 | node_modules/ 3 | coverage/ 4 | -------------------------------------------------------------------------------- /dev-portfolio/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | plugins: ['prettier'], 4 | extends: [ 5 | 'eslint:recommended', 6 | 'prettier', 7 | 'plugin:jest-playwright/recommended', 8 | ], 9 | rules: { 10 | 'global-require': 'off', 11 | 'import/no-extraneous-dependencies': 'off', 12 | 'import/no-dynamic-require': 'off', 13 | 'import/no-unresolved': 'off', 14 | 'prettier/prettier': 'error', 15 | }, 16 | parserOptions: { 17 | ecmaVersion: 8, 18 | sourceType: 'module', 19 | ecmaFeatures: { 20 | jsx: true, 21 | modules: true, 22 | experimentalObjectRestSpread: true, 23 | }, 24 | }, 25 | env: { 26 | node: true, 27 | jest: true, 28 | browser: true, 29 | }, 30 | globals: { 31 | page: true, 32 | browser: true, 33 | context: true, 34 | }, 35 | } 36 | -------------------------------------------------------------------------------- /dev-portfolio/.gitignore: -------------------------------------------------------------------------------- 1 | # Log files 2 | logs 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | *.log 7 | 8 | # DS_Store 9 | *.DS_Store 10 | 11 | # virtual machine crash logs 12 | hs_err_pid* 13 | 14 | # node_modules 15 | **/node_modules 16 | 17 | # Optional npm cache directory 18 | **/.npm 19 | 20 | # files 21 | **/.DS_Store 22 | **/.idea 23 | 24 | # Extra folders 25 | .tmp/ 26 | tests_output/ 27 | **/apps 28 | **/videos/ 29 | **/cypress/screenshots/** 30 | **/build/** 31 | 32 | #next 33 | .next** 34 | .next/.** 35 | 36 | **/coverage/** -------------------------------------------------------------------------------- /dev-portfolio/.happo.js: -------------------------------------------------------------------------------- 1 | const { RemoteBrowserTarget } = require("happo.io"); 2 | 3 | module.exports = { 4 | apiKey: process.env.HAPPO_API_KEY, 5 | apiSecret: process.env.HAPPO_API_SECRET, 6 | project: "dev-portfolio-tutorial", 7 | targets: { 8 | "chrome-1080p": new RemoteBrowserTarget("chrome", { 9 | viewport: "1920x1080", 10 | freezeAnimations: "last-frame", 11 | }), 12 | // all viewports https://viewportsizer.com/devices/ 13 | // Samsung Galaxy S10+, S10 14 | "chrome-galaxy-s10": new RemoteBrowserTarget("chrome", { 15 | viewport: "360x740", 16 | freezeAnimations: "last-frame", 17 | }), 18 | // iphone viewports https://yesviz.com/iphones.php 19 | "safari-1080p": new RemoteBrowserTarget("safari", { 20 | viewport: "1920x1080", 21 | freezeAnimations: "last-frame", 22 | }), 23 | // iphone 12 pro 24 | "safari-iphone-12-pro": new RemoteBrowserTarget("safari", { 25 | viewport: "390x844", 26 | freezeAnimations: "last-frame", 27 | }), 28 | // iphone 12 pro 29 | "chrome-650": new RemoteBrowserTarget("chrome", { 30 | viewport: "650x415", 31 | freezeAnimations: "last-frame", 32 | }), 33 | // only works on public urls 34 | // pages: [{ url: 'http://localhost:3000/', title: 'Home Page' }], 35 | }, 36 | }; 37 | -------------------------------------------------------------------------------- /dev-portfolio/.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /dev-portfolio/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all", 4 | "semi": false, 5 | "tabWidth": 4 6 | } 7 | -------------------------------------------------------------------------------- /dev-portfolio/cypress.config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require('cypress') 2 | 3 | module.exports = defineConfig({ 4 | e2e: { 5 | // We've imported your old cypress plugins here. 6 | // You may want to clean this up later by importing these. 7 | setupNodeEvents(on, config) { 8 | return require('./cypress/plugins/index.js')(on, config) 9 | }, 10 | baseUrl: 'http://localhost:3000', 11 | }, 12 | }) 13 | -------------------------------------------------------------------------------- /dev-portfolio/cypress/e2e/SocialDetails.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe("SocialDetails", () => { 4 | beforeEach(() => { 5 | cy.visit("/"); 6 | cy.get("body").should("be.visible"); 7 | }); 8 | 9 | it("have correct social links", () => { 10 | cy.get("[data-testid='github']").should( 11 | "have.attr", 12 | "href", 13 | "https://github.com/nadvolod" 14 | ); 15 | cy.get("[data-testid='youtube']").should( 16 | "have.attr", 17 | "href", 18 | "https://www.youtube.com/ultimateqa" 19 | ); 20 | cy.get("[data-testid='linkedin']").should( 21 | "have.attr", 22 | "href", 23 | "https://www.linkedin.com/in/nikolayadvolodkin/" 24 | ); 25 | cy.get("[data-testid='twitter']").should( 26 | "have.attr", 27 | "href", 28 | "https://twitter.com/Nikolay_A00" 29 | ); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /dev-portfolio/cypress/e2e/homepage.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe('Homepage', () => { 4 | beforeEach(() => { 5 | cy.visit('/') 6 | }) 7 | 8 | it('has maintained lighthouse metrics', () => { 9 | cy.lighthouse({ 10 | accessibility: 77, 11 | 'best-practices': 87, 12 | seo: 75, 13 | pwa: 20, 14 | performance: 30, 15 | }) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /dev-portfolio/cypress/e2e/visual.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe("visual tests", () => { 4 | beforeEach(() => { 5 | cy.visit("/"); 6 | cy.get("body").should("be.visible"); 7 | cy.get("body").should("not.have.css", "display", "none"); 8 | }); 9 | 10 | it("display the home page correctly", () => { 11 | cy.get("body").happoScreenshot(); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /dev-portfolio/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /dev-portfolio/cypress/plugins/index.js: -------------------------------------------------------------------------------- 1 | /// 2 | // *********************************************************** 3 | // This example plugins/index.js can be used to load plugins 4 | // 5 | // You can change the location of this file or turn off loading 6 | // the plugins file with the 'pluginsFile' configuration option. 7 | // 8 | // You can read more here: 9 | // https://on.cypress.io/plugins-guide 10 | // *********************************************************** 11 | 12 | // This function is called when a project is opened or re-opened (e.g. due to 13 | // the project's config changing) 14 | 15 | /** 16 | * @type {Cypress.PluginConfig} 17 | */ 18 | const happoTask = require("happo-cypress/task"); 19 | const { lighthouse, prepareAudit } = require("@cypress-audit/lighthouse"); 20 | 21 | module.exports = (on) => { 22 | happoTask.register(on); 23 | on("before:browser:launch", (browser = {}, launchOptions) => { 24 | prepareAudit(launchOptions); 25 | }); 26 | 27 | on("task", { 28 | lighthouse: lighthouse((lighthouseReport) => { 29 | console.log(lighthouseReport); // raw lighthouse reports 30 | }), 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /dev-portfolio/cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add('login', (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This will overwrite an existing command -- 25 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) 26 | import "happo-cypress"; 27 | import "@cypress-audit/lighthouse/commands"; 28 | -------------------------------------------------------------------------------- /dev-portfolio/cypress/support/e2e.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /dev-portfolio/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "portfolio_nextjs", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build && next export", 8 | "start": "next start", 9 | "test:visual": "npx happo-e2e -- cypress run --spec 'cypress/integration/visual.spec.js'", 10 | "test:jest": "jest", 11 | "test.lighthouse.ci": "npx cypress run --spec '**/integration/homepage.spec.js' --browser=chrome", 12 | "test.lighthouse": "npx cypress run --spec '**/integration/homepage.spec.js' --browser=chrome --headed", 13 | "happo": "happo", 14 | "happo-ci-github-actions": "happo-ci-github-actions" 15 | }, 16 | "dependencies": { 17 | "next": "^12.1.0", 18 | "react": "^17.0.2", 19 | "react-dom": "^17.0.2", 20 | "react-icons": "^4.2.0", 21 | "styled-components": "^5.3.0", 22 | "styled-normalize": "^8.0.7" 23 | }, 24 | "devDependencies": { 25 | "cypress": "^10.8.0", 26 | "cypress-audit": "^1.1.0", 27 | "happo-cypress": "^3.0.1", 28 | "happo-e2e": "^1.0.3", 29 | "happo.io": "^7.0.2", 30 | "jest": "^27.5.1" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /dev-portfolio/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/dev-portfolio/public/favicon.ico -------------------------------------------------------------------------------- /dev-portfolio/public/images/birthday-reminder.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/dev-portfolio/public/images/birthday-reminder.webp -------------------------------------------------------------------------------- /dev-portfolio/public/images/comparison-settings.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/dev-portfolio/public/images/comparison-settings.webp -------------------------------------------------------------------------------- /dev-portfolio/public/images/github-users-search.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/dev-portfolio/public/images/github-users-search.webp -------------------------------------------------------------------------------- /dev-portfolio/public/images/portfolio.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/dev-portfolio/public/images/portfolio.webp -------------------------------------------------------------------------------- /dev-portfolio/public/images/sauce-bindings.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/dev-portfolio/public/images/sauce-bindings.webp -------------------------------------------------------------------------------- /dev-portfolio/public/images/tic-tac-toe.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/dev-portfolio/public/images/tic-tac-toe.webp -------------------------------------------------------------------------------- /dev-portfolio/public/images/ultimateqa.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/dev-portfolio/public/images/ultimateqa.webp -------------------------------------------------------------------------------- /dev-portfolio/public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /dev-portfolio/src/components/Acomplishments/Acomplishments.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { 4 | Section, 5 | SectionDivider, 6 | SectionTitle, 7 | } from "../../styles/GlobalComponents"; 8 | import { Box, Boxes, BoxNum, BoxText } from "./AcomplishmentsStyles"; 9 | 10 | const data = [ 11 | { number: "100,000", text: "SDETs Trained" }, 12 | { number: 50, text: "Open Source Projects" }, 13 | { number: 5, text: "Years Being Awared Top Automation Engineer" }, 14 | { number: 250, text: "Github Stars" }, 15 | ]; 16 | 17 | const Acomplishments = () => ( 18 |
19 | Personal Accomplishments 20 | 21 | {data.map((card, index) => ( 22 | 23 | {card.number}+ 24 | {card.text} 25 | 26 | ))} 27 | 28 |
29 | ); 30 | 31 | export default Acomplishments; 32 | -------------------------------------------------------------------------------- /dev-portfolio/src/components/Footer/Footer.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import SocialDetails from "../Header/SocialDetails"; 3 | 4 | import { 5 | CompanyContainer, 6 | FooterWrapper, 7 | LinkColumn, 8 | LinkItem, 9 | LinkList, 10 | LinkTitle, 11 | Slogan, 12 | SocialIconsContainer, 13 | } from "./FooterStyles"; 14 | 15 | const Footer = () => { 16 | return ( 17 | 18 | 19 | 20 | Email 21 | 22 | click to email me 23 | 24 | 25 | 26 | 27 | 28 | Innovation 29 | 30 | 31 | 32 | 33 | ); 34 | }; 35 | 36 | export default Footer; 37 | -------------------------------------------------------------------------------- /dev-portfolio/src/components/Header/Header.js: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import React from "react"; 3 | import { AiFillGithub, AiFillInstagram, AiFillLinkedin } from "react-icons/ai"; 4 | import { DiCssdeck } from "react-icons/di"; 5 | 6 | import { 7 | Container, 8 | Div1, 9 | Div2, 10 | Div3, 11 | NavLink, 12 | SocialIcons, 13 | } from "./HeaderStyles"; 14 | import SocialDetails from "./SocialDetails"; 15 | 16 | const Header = () => ( 17 | 18 | 19 | 20 | 28 | 29 | Portfolio 30 | 31 | 32 | 33 | 34 |
  • 35 | 36 | Projects 37 | 38 |
  • 39 |
  • 40 | 41 | Technologies 42 | 43 |
  • 44 |
  • 45 | 46 | About 47 | 48 |
  • 49 |
    50 | 51 |
    52 | ); 53 | 54 | export default Header; 55 | -------------------------------------------------------------------------------- /dev-portfolio/src/components/Header/SocialDetails.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Div3, SocialIcons } from "./HeaderStyles"; 3 | import { 4 | AiFillGithub, 5 | AiFillLinkedin, 6 | AiFillTwitterCircle, 7 | AiFillYoutube, 8 | } from "react-icons/ai"; 9 | import { socialUrls } from "../../constants/socialUrls"; 10 | 11 | const SocialDetails = () => { 12 | return ( 13 | <> 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | ); 30 | }; 31 | 32 | export default SocialDetails; 33 | -------------------------------------------------------------------------------- /dev-portfolio/src/components/Hero/Hero.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { 4 | Section, 5 | SectionText, 6 | SectionTitle, 7 | } from "../../styles/GlobalComponents"; 8 | import Button from "../../styles/GlobalComponents/Button"; 9 | import { LeftSection } from "./HeroStyles"; 10 | 11 | const Hero = (props) => ( 12 |
    13 | 14 | 15 | Nikolay Advolodkin 16 | 17 | 18 | My life's mission is to create world-class software 19 | 20 | 21 | 22 |
    23 | ); 24 | 25 | export default Hero; 26 | -------------------------------------------------------------------------------- /dev-portfolio/src/components/Hero/HeroStyles.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const LeftSection = styled.div` 4 | width: 100%; 5 | @media ${(props) => props.theme.breakpoints.sm} { 6 | width: 80%; 7 | display: flex; 8 | flex-direction: column; 9 | 10 | margin: 0 auto; 11 | } 12 | @media ${(props) => props.theme.breakpoints.md} { 13 | width: 100%; 14 | display: flex; 15 | flex-direction: column; 16 | 17 | margin: 0 auto; 18 | } 19 | `; 20 | -------------------------------------------------------------------------------- /dev-portfolio/src/components/NavDropDown/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { AiFillPhone, AiOutlineMail } from 'react-icons/ai' 3 | import { FaLocationArrow } from "react-icons/fa" 4 | 5 | import { DropDownContainer, DropDownIcon, DropDownItem, DropDownItemDesc, DropDownItemTitle, DropDownTextContainer } from './NavDropDown' 6 | 7 | const NavDropDown = (props) => ( 8 |
    9 | NavDropDown 10 |
    11 | ); 12 | 13 | export default NavDropDown 14 | -------------------------------------------------------------------------------- /dev-portfolio/src/constants/socialUrls.js: -------------------------------------------------------------------------------- 1 | const socialUrls = { 2 | github: "https://github.com/nadvolod", 3 | linkedIn: "https://www.linkedin.com/in/nikolayadvolodkin/", 4 | twitter: "https://twitter.com/Nikolay_A00", 5 | youtube: "https://www.youtube.com/ultimateqa", 6 | }; 7 | 8 | module.exports = { socialUrls }; 9 | -------------------------------------------------------------------------------- /dev-portfolio/src/constants/timelineData.js: -------------------------------------------------------------------------------- 1 | export const timelineData = [ 2 | { 3 | year: 2022, 4 | text: 'Testing for Good raises $12k+ for NGOs + plants >1000 trees', 5 | }, 6 | { 7 | year: 2021, 8 | text: 'Created Testing for Good. Raised $10k+ for NGOs + trained 100s of SDETs.', 9 | }, 10 | { 11 | year: 2018, 12 | text: 'Sr Solutions Architect @ Sauce Labs. Maintain most strategic accounts.', 13 | }, 14 | { year: 2016, text: 'Test Automation Lead' }, 15 | { year: 2015, text: 'Founded UltimateQA.com' }, 16 | { year: 2013, text: 'Sr SDET at IXI' }, 17 | { year: 2008, text: 'Started as QA Engineer' }, 18 | ] 19 | -------------------------------------------------------------------------------- /dev-portfolio/src/layout/Layout.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import Footer from '../components/Footer/Footer' 4 | import Header from '../components/Header/Header' 5 | import { Container } from './LayoutStyles' 6 | 7 | export const Layout = ({children}) => { 8 | return ( 9 | 10 |
    11 |
    {children}
    12 |
    13 | 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /dev-portfolio/src/layout/LayoutStyles.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Container = styled.div` 4 | max-width: 1280px; 5 | width: 100%; 6 | margin: auto; 7 | `; 8 | -------------------------------------------------------------------------------- /dev-portfolio/src/pages/_app.js: -------------------------------------------------------------------------------- 1 | import Theme from '../styles/theme'; 2 | 3 | export default function App({ Component, pageProps }) { 4 | return ( 5 | <> 6 | 7 | 8 | 9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /dev-portfolio/src/pages/_document.js: -------------------------------------------------------------------------------- 1 | import Document, { Head, Html, Main, NextScript } from 'next/document' 2 | import { ServerStyleSheet } from 'styled-components' 3 | 4 | export default class MyDocument extends Document { 5 | static async getInitialProps(ctx) { 6 | const sheet = new ServerStyleSheet() 7 | const originalRenderPage = ctx.renderPage 8 | 9 | try { 10 | ctx.renderPage = () => 11 | originalRenderPage({ 12 | enhanceApp: (App) => (props) => 13 | sheet.collectStyles(), 14 | }) 15 | 16 | const initialProps = await Document.getInitialProps(ctx) 17 | return { 18 | ...initialProps, 19 | styles: ( 20 | <> 21 | {initialProps.styles} 22 | {sheet.getStyleElement()} 23 | 24 | ), 25 | } 26 | } finally { 27 | sheet.seal() 28 | } 29 | } 30 | render() { 31 | return ( 32 | 33 | 34 | 35 | 36 | 37 |
    38 | 39 | 40 | 41 | ); 42 | } 43 | } -------------------------------------------------------------------------------- /dev-portfolio/src/pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default (req, res) => { 4 | res.status(200).json({ name: 'John Doe' }) 5 | } -------------------------------------------------------------------------------- /dev-portfolio/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import Acomplishments from "../components/Acomplishments/Acomplishments"; 2 | import BgAnimation from "../components/BackgroundAnimation/BackgroundAnimation"; 3 | import Hero from "../components/Hero/Hero"; 4 | import Projects from "../components/Projects/Projects"; 5 | import Technologies from "../components/Technologies/Technologies"; 6 | import Timeline from "../components/TimeLine/TimeLine"; 7 | import { Layout } from "../layout/Layout"; 8 | import { Section } from "../styles/GlobalComponents"; 9 | 10 | const Home = () => { 11 | return ( 12 | 13 | {/*
    */} 14 | 15 | {/* */} 16 | {/*
    */} 17 | 18 | 19 | 20 | 21 |
    22 | ); 23 | }; 24 | 25 | export default Home; 26 | -------------------------------------------------------------------------------- /dev-portfolio/src/styles/GlobalComponents/Button.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { ButtonBack, ButtonFront } from './index' 4 | 5 | const Button = (props) => ( 6 | {props.children} 7 | {props.children} 8 | 9 | ); 10 | 11 | export default Button 12 | -------------------------------------------------------------------------------- /dev-portfolio/src/styles/globals.js: -------------------------------------------------------------------------------- 1 | import { createGlobalStyle } from 'styled-components'; 2 | import { normalize } from 'styled-normalize'; 3 | 4 | const GlobalStyles = createGlobalStyle` 5 | ${normalize}; 6 | 7 | * { 8 | box-sizing: border-box; 9 | margin: 0; 10 | padding: 0; 11 | } 12 | html { 13 | font-size: 62.5%; 14 | scroll-behavior: smooth; 15 | 16 | } 17 | body { 18 | font-family: ${props => props.theme.fonts.main}; 19 | font-size: 1.6rem; 20 | background: ${props => props.theme.colors.background1}; 21 | color: ${props => props.theme.colors.primary1}; 22 | cursor: default; 23 | 24 | } 25 | h1,h2,h3,h4,h5,h6,button { 26 | font-family: ${props => props.theme.fonts.title}; 27 | } 28 | a { 29 | text-decoration: none; 30 | } 31 | li{ 32 | list-style: none; 33 | } 34 | 35 | `; 36 | 37 | export default GlobalStyles; -------------------------------------------------------------------------------- /dev-portfolio/src/styles/theme.js: -------------------------------------------------------------------------------- 1 | import { ThemeProvider } from 'styled-components'; 2 | 3 | import theme from "../themes/default"; 4 | import GlobalStyles from './globals'; 5 | 6 | const Theme = ({ children }) => ( 7 | 8 | 9 | {children} 10 | 11 | ); 12 | 13 | export default Theme; -------------------------------------------------------------------------------- /dev-portfolio/src/themes/default.js: -------------------------------------------------------------------------------- 1 | export default { 2 | // Temp fonts 3 | fonts: { 4 | title: "Space Grotesk, sans-serif", 5 | main: "Space Grotesk, sans-serif" 6 | }, 7 | // Colors for layout 8 | colors: { 9 | primary1: "hsl(204,23.8%,95.9%)", 10 | background1: "#0F1624", 11 | accent1: "hsl(34.9,98.6%,72.9%)", 12 | button: "hsl(205.1,100%,36.1%)", 13 | background2: "hsl(232.7,27.3%,23.7%)", 14 | }, 15 | // Breakpoints for responsive design 16 | breakpoints: { 17 | sm: 'screen and (max-width: 640px)', 18 | md: 'screen and (max-width: 768px)', 19 | lg: 'screen and (max-width: 1024px)', 20 | xl: 'screen and (max-width: 1280px)' 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /github-user-search/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /github-user-search/end/cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseUrl": "http://localhost:3000" 3 | } 4 | -------------------------------------------------------------------------------- /github-user-search/end/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /github-user-search/end/cypress/integration/Search.spec.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe('search page', () => { 4 | beforeEach(() => { 5 | cy.visit('/'); 6 | }); 7 | it('performs search w/ valid user', () => { 8 | const typedText = 'nadvolod'; 9 | cy.get('[data-testid="search-bar"]') 10 | .type(typedText) 11 | .should('have.value', typedText); 12 | 13 | cy.get('[data-testid="rate-limit"]').then(($count) => { 14 | const requestsLimit = $count.text(); 15 | cy.get('form').submit(); 16 | 17 | cy.get('[data-testid="rate-limit"]').should(($requestsRemaining) => { 18 | expect($requestsRemaining.text()).not.to.eq(requestsLimit); 19 | }); 20 | }); 21 | }); 22 | 23 | it('performs search w/ invalid user', () => { 24 | const typedText = '674foo987'; 25 | cy.get('[data-testid="search-bar"]') 26 | .type(typedText) 27 | .should('have.value', typedText); 28 | 29 | cy.get('[data-testid="rate-limit"]').then(($count) => { 30 | const requestsLimit = $count.text(); 31 | cy.get('form').submit(); 32 | 33 | cy.get('[data-testid="rate-limit"]').should(($requestsRemaining) => { 34 | expect($requestsRemaining.text()).not.to.eq(requestsLimit); 35 | }); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /github-user-search/end/cypress/integration/error.spec.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe('error page', () => { 4 | beforeEach(() => { 5 | cy.visit('/5678'); 6 | }); 7 | 8 | it('displays two todo items by default', () => { 9 | cy.get('[data-testid="back-home"]') 10 | .should('have.attr', 'href') 11 | .and('eq', '/'); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /github-user-search/end/cypress/plugins/index.js: -------------------------------------------------------------------------------- 1 | /// 2 | // *********************************************************** 3 | // This example plugins/index.js can be used to load plugins 4 | // 5 | // You can change the location of this file or turn off loading 6 | // the plugins file with the 'pluginsFile' configuration option. 7 | // 8 | // You can read more here: 9 | // https://on.cypress.io/plugins-guide 10 | // *********************************************************** 11 | 12 | // This function is called when a project is opened or re-opened (e.g. due to 13 | // the project's config changing) 14 | 15 | /** 16 | * @type {Cypress.PluginConfig} 17 | */ 18 | // eslint-disable-next-line no-unused-vars 19 | module.exports = (on, config) => { 20 | // `on` is used to hook into various events Cypress emits 21 | // `config` is the resolved Cypress config 22 | } 23 | -------------------------------------------------------------------------------- /github-user-search/end/cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add('login', (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This will overwrite an existing command -- 25 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) 26 | -------------------------------------------------------------------------------- /github-user-search/end/cypress/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /github-user-search/end/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "github-search", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@auth0/auth0-react": "^1.8.0", 7 | "@testing-library/jest-dom": "^5.16.1", 8 | "@testing-library/react": "^12.1.2", 9 | "@testing-library/user-event": "^13.5.0", 10 | "axios": "^0.24.0", 11 | "cypress": "^9.3.1", 12 | "react": "^17.0.2", 13 | "react-dom": "^17.0.2", 14 | "react-icons": "^4.3.1", 15 | "react-router-dom": "^6.2.1", 16 | "react-scripts": "5.0.0", 17 | "styled-components": "^5.3.3" 18 | }, 19 | "scripts": { 20 | "start": "react-scripts start", 21 | "build": "CI= react-scripts build", 22 | "test": "react-scripts test", 23 | "eject": "react-scripts eject", 24 | "test:visual": "wdio run wdio.conf.js", 25 | "test:cy": "npx cypress run" 26 | }, 27 | "eslintConfig": { 28 | "extends": "react-app" 29 | }, 30 | "browserslist": { 31 | "production": [ 32 | ">0.2%", 33 | "not dead", 34 | "not op_mini all" 35 | ], 36 | "development": [ 37 | "last 1 chrome version", 38 | "last 1 firefox version", 39 | "last 1 safari version" 40 | ] 41 | }, 42 | "devDependencies": { 43 | "@wdio/cli": "^7.16.13", 44 | "@wdio/dot-reporter": "^7.16.13", 45 | "@wdio/local-runner": "^7.16.13", 46 | "@wdio/mocha-framework": "^7.16.13", 47 | "@wdio/sauce-service": "^7.16.13" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /github-user-search/end/public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /github-user-search/end/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/github-user-search/end/public/favicon.ico -------------------------------------------------------------------------------- /github-user-search/end/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/github-user-search/end/public/logo192.png -------------------------------------------------------------------------------- /github-user-search/end/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/github-user-search/end/public/logo512.png -------------------------------------------------------------------------------- /github-user-search/end/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /github-user-search/end/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /github-user-search/end/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Dashboard, Login, PrivateRoute, AuthWrapper, Error } from './pages'; 3 | import { BrowserRouter, Routes, Route } from 'react-router-dom'; 4 | 5 | function App() { 6 | return ( 7 | 8 | 9 | }> 10 | }> 11 | }> 12 | 13 | 14 | ); 15 | } 16 | 17 | export default App; 18 | -------------------------------------------------------------------------------- /github-user-search/end/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | import Error from './pages/Error'; 4 | 5 | test('404 page back-home button back home', () => { 6 | const { getByTestId } = render(); 7 | const button = getByTestId('back-home'); 8 | expect(button.href).toContain('http://localhost'); 9 | }); 10 | -------------------------------------------------------------------------------- /github-user-search/end/src/components/Navbar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import { useAuth0 } from '@auth0/auth0-react'; 4 | 5 | const Navbar = () => { 6 | return ( 7 | 8 |

    Welcome

    9 |
    10 | ); 11 | }; 12 | 13 | const Wrapper = styled.nav` 14 | padding: 1.5rem; 15 | margin-bottom: 4rem; 16 | background: var(--clr-white); 17 | text-align: center; 18 | display: grid; 19 | grid-template-columns: auto auto 100px; 20 | justify-content: center; 21 | align-items: center; 22 | gap: 1.5rem; 23 | h4 { 24 | margin-bottom: 0; 25 | font-weight: 400; 26 | } 27 | img { 28 | width: 35px !important; 29 | height: 35px; 30 | border-radius: 50%; 31 | object-fit: cover; 32 | } 33 | button { 34 | background: transparent; 35 | border: transparent; 36 | font-size: 1.2rem; 37 | text-transform: capitalize; 38 | letter-spacing: var(--spacing); 39 | color: var(--clr-grey-5); 40 | cursor: pointer; 41 | } 42 | `; 43 | 44 | export default Navbar; 45 | -------------------------------------------------------------------------------- /github-user-search/end/src/components/Repos.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import { GithubContext } from '../context/context'; 4 | const Repos = () => { 5 | return

    repos component

    ; 6 | }; 7 | 8 | const Wrapper = styled.div` 9 | display: grid; 10 | justify-items: center; 11 | gap: 2rem; 12 | @media (min-width: 800px) { 13 | grid-template-columns: 1fr 1fr; 14 | } 15 | 16 | @media (min-width: 1200px) { 17 | grid-template-columns: 2fr 3fr; 18 | } 19 | 20 | div { 21 | width: 100% !important; 22 | } 23 | .fusioncharts-container { 24 | width: 100% !important; 25 | } 26 | svg { 27 | width: 100% !important; 28 | border-radius: var(--radius) !important; 29 | } 30 | `; 31 | 32 | export default Repos; 33 | -------------------------------------------------------------------------------- /github-user-search/end/src/components/User.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import Card from './Card'; 4 | import Followers from './Followers'; 5 | const User = () => { 6 | return ( 7 |
    8 | 9 | 10 | 11 | 12 |
    13 | ); 14 | }; 15 | 16 | const Wrapper = styled.div` 17 | padding-top: 2rem; 18 | display: grid; 19 | gap: 3rem 2rem; 20 | @media (min-width: 992px) { 21 | grid-template-columns: 1fr 1fr; 22 | } 23 | /* align-items: start; */ 24 | `; 25 | 26 | export default User; 27 | -------------------------------------------------------------------------------- /github-user-search/end/src/components/index.js: -------------------------------------------------------------------------------- 1 | import Info from "./Info"; 2 | import Repos from "./Repos"; 3 | import User from "./User"; 4 | import Search from "./Search"; 5 | import Navbar from "./Navbar"; 6 | 7 | export { Info, Repos, User, Search, Navbar }; 8 | -------------------------------------------------------------------------------- /github-user-search/end/src/context/mockData.js/mockUser.js: -------------------------------------------------------------------------------- 1 | export default { 2 | login: 'nadvolod', 3 | id: 2136245, 4 | node_id: 'MDQ6VXNlcjIxMzYyNDU=', 5 | avatar_url: 'https://avatars.githubusercontent.com/u/2136245?v=4', 6 | gravatar_id: '', 7 | url: 'https://api.github.com/users/nadvolod', 8 | html_url: 'https://github.com/nadvolod', 9 | followers_url: 'https://api.github.com/users/nadvolod/followers', 10 | following_url: 'https://api.github.com/users/nadvolod/following{/other_user}', 11 | gists_url: 'https://api.github.com/users/nadvolod/gists{/gist_id}', 12 | starred_url: 'https://api.github.com/users/nadvolod/starred{/owner}{/repo}', 13 | subscriptions_url: 'https://api.github.com/users/nadvolod/subscriptions', 14 | organizations_url: 'https://api.github.com/users/nadvolod/orgs', 15 | repos_url: 'https://api.github.com/users/nadvolod/repos', 16 | events_url: 'https://api.github.com/users/nadvolod/events{/privacy}', 17 | received_events_url: 'https://api.github.com/users/nadvolod/received_events', 18 | type: 'User', 19 | site_admin: false, 20 | name: 'Nikolay Advolodkin', 21 | company: 'Sauce Labs', 22 | blog: 'www.ultimateqa.com', 23 | location: 'Miami, FL', 24 | email: null, 25 | hireable: null, 26 | bio: 'My mission is to train 1 Million SDETs!', 27 | twitter_username: 'nikolay_a00', 28 | public_repos: 107, 29 | public_gists: 111, 30 | followers: 246, 31 | following: 4, 32 | created_at: '2012-08-11T17:44:01Z', 33 | updated_at: '2022-01-24T19:17:44Z', 34 | }; 35 | -------------------------------------------------------------------------------- /github-user-search/end/src/images/preloader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/github-user-search/end/src/images/preloader.gif -------------------------------------------------------------------------------- /github-user-search/end/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | import { GithubProvider } from './context/context'; 7 | import { Auth0Provider } from '@auth0/auth0-react'; 8 | 9 | ReactDOM.render( 10 | 11 | 12 | 13 | 14 | , 15 | document.getElementById('root') 16 | ); 17 | 18 | // If you want your app to work offline and load faster, you can change 19 | // unregister() to register() below. Note this comes with some pitfalls. 20 | // Learn more about service workers: https://bit.ly/CRA-PWA 21 | serviceWorker.unregister(); 22 | -------------------------------------------------------------------------------- /github-user-search/end/src/pages/AuthWrapper.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useAuth0 } from '@auth0/auth0-react'; 3 | import loadingGif from '../images/preloader.gif'; 4 | import styled from 'styled-components'; 5 | function AuthWrapper() { 6 | return

    authwrapper component

    ; 7 | } 8 | 9 | const Wrapper = styled.section` 10 | min-height: 100vh; 11 | display: grid; 12 | place-items: center; 13 | img { 14 | width: 150px; 15 | } 16 | `; 17 | 18 | export default AuthWrapper; 19 | -------------------------------------------------------------------------------- /github-user-search/end/src/pages/Dashboard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Info, User, Search, Navbar } from '../components'; 3 | import loadingImage from '../images/preloader.gif'; 4 | import { GithubContext } from '../context/context'; 5 | const Dashboard = () => { 6 | return ( 7 |
    8 | 9 | 10 | 11 | 12 |
    13 | ); 14 | }; 15 | 16 | export default Dashboard; 17 | -------------------------------------------------------------------------------- /github-user-search/end/src/pages/Error.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import { Link } from 'react-router-dom'; 4 | const Error = () => { 5 | return ( 6 | 7 |

    404

    8 |

    Sorry, page not found

    9 | 10 | back home 11 | 12 |
    13 | ); 14 | }; 15 | const Wrapper = styled.section` 16 | min-height: 100vh; 17 | display: grid; 18 | place-items: center; 19 | background: var(--clr-primary-10); 20 | text-align: center; 21 | h1 { 22 | font-size: 10rem; 23 | } 24 | h3 { 25 | color: var(--clr-grey-3); 26 | margin-bottom: 1.5rem; 27 | } 28 | `; 29 | export default Error; 30 | -------------------------------------------------------------------------------- /github-user-search/end/src/pages/Login.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useAuth0 } from '@auth0/auth0-react'; 3 | import styled from 'styled-components'; 4 | import loginImg from '../images/login-img.svg'; 5 | const Login = () => { 6 | return ( 7 | 8 |
    9 | github-user 10 |

    github user

    11 | 12 |
    13 |
    14 | ); 15 | }; 16 | const Wrapper = styled.section` 17 | min-height: 100vh; 18 | display: grid; 19 | place-items: center; 20 | .container { 21 | width: 90vw; 22 | max-width: 600px; 23 | text-align: center; 24 | } 25 | img { 26 | margin-bottom: 2rem; 27 | } 28 | h1 { 29 | margin-bottom: 1.5rem; 30 | } 31 | `; 32 | export default Login; 33 | -------------------------------------------------------------------------------- /github-user-search/end/src/pages/PrivateRoute.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Route, Redirect } from 'react-router-dom'; 3 | import { useAuth0 } from '@auth0/auth0-react'; 4 | 5 | const PrivateRoute = () => { 6 | return

    private route component

    ; 7 | }; 8 | export default PrivateRoute; 9 | -------------------------------------------------------------------------------- /github-user-search/end/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import Dashboard from "./Dashboard"; 2 | import Login from "./Login"; 3 | import AuthWrapper from "./AuthWrapper"; 4 | import PrivateRoute from "./PrivateRoute"; 5 | import Error from "./Error"; 6 | 7 | export { Dashboard, Login, AuthWrapper, PrivateRoute, Error }; 8 | -------------------------------------------------------------------------------- /github-user-search/end/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | -------------------------------------------------------------------------------- /github-user-search/end/test/specs/visual.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | describe('Visual checks', () => { 3 | it('pages render correctly', async () => { 4 | await browser.url(''); 5 | await browser.execute('/*@visual.init*/', 'Visual Test'); 6 | await browser.execute('/*@visual.snapshot*/', 'Dashboard Page', { 7 | ignore: '[data-testid="rate-limit"]', 8 | }); 9 | 10 | await browser.url('/login'); 11 | const githubImage = await $('[alt="github-user"]'); 12 | await githubImage.waitForDisplayed(); 13 | await browser.execute('/*@visual.snapshot*/', 'Login Page'); 14 | 15 | await browser.url('/foobar'); 16 | //await browser.debug(); 17 | // await browser.execute('sauce: break'); 18 | const button = await $('[data-testid="back-home"]'); 19 | await button.waitForDisplayed(); 20 | await browser.execute('/*@visual.snapshot*/', 'Error Page'); 21 | 22 | const result = await browser.execute('/*@visual.end*/'); 23 | expect(result.message).toBeNull(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /github-user-search/end/wdio.functional.conf.js: -------------------------------------------------------------------------------- 1 | exports.config = { 2 | region: process.env.REGION || 'us', 3 | services: [ 4 | [ 5 | 'sauce', 6 | { 7 | sauceConnect: true, 8 | sauceConnectOpts: { 9 | noSslBumpDomains: 'all', 10 | }, 11 | }, 12 | ], 13 | ], 14 | specs: ['./test/specs/**/functional*.js'], 15 | // Patterns to exclude. 16 | exclude: [ 17 | // 'path/to/excluded/files' 18 | ], 19 | maxInstances: 10, 20 | user: process.env.SAUCE_USERNAME, 21 | key: process.env.SAUCE_ACCESS_KEY, 22 | capabilities: [ 23 | //Desktop A 28%: https://www.w3schools.com/browsers/browsers_display.asp 24 | { 25 | browserName: 'chrome', 26 | platformName: 'windows 10', 27 | browserVersion: 'latest', 28 | acceptInsecureCerts: true, 29 | 'sauce:options': { 30 | build: `Sauce Labs W3C Desktop build-${new Date().getTime()}`, 31 | screenResolution: '1440x900', 32 | }, 33 | }, 34 | ], 35 | // 36 | // =================== 37 | // Test Configurations 38 | // =================== 39 | // Level of logging verbosity: trace | debug | info | warn | error | silent 40 | logLevel: 'error', 41 | bail: 0, 42 | baseUrl: 'http://localhost:3000', 43 | waitforTimeout: 10000, 44 | connectionRetryTimeout: 120000, 45 | connectionRetryCount: 3, 46 | framework: 'mocha', 47 | reporters: ['dot'], 48 | mochaOpts: { 49 | ui: 'bdd', 50 | timeout: 120000, 51 | }, 52 | }; 53 | -------------------------------------------------------------------------------- /github-user-search/start/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "github-search", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@auth0/auth0-react": "^1.8.0", 7 | "@testing-library/jest-dom": "^5.16.1", 8 | "@testing-library/react": "^12.1.2", 9 | "@testing-library/user-event": "^13.5.0", 10 | "axios": "^0.24.0", 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2", 13 | "react-icons": "^4.3.1", 14 | "react-router-dom": "^6.2.1", 15 | "react-scripts": "5.0.0", 16 | "styled-components": "^5.3.3" 17 | }, 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build": "CI= react-scripts build", 21 | "test": "react-scripts test", 22 | "eject": "react-scripts eject", 23 | "test:visual": "wdio run wdio.conf.js", 24 | "test:func": "wdio run wdio.functional.conf.js" 25 | }, 26 | "eslintConfig": { 27 | "extends": "react-app" 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.2%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | }, 41 | "devDependencies": { 42 | "@wdio/cli": "^7.16.13", 43 | "@wdio/dot-reporter": "^7.16.13", 44 | "@wdio/local-runner": "^7.16.13", 45 | "@wdio/mocha-framework": "^7.16.13", 46 | "@wdio/sauce-service": "^7.16.13" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /github-user-search/start/public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /github-user-search/start/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/github-user-search/start/public/favicon.ico -------------------------------------------------------------------------------- /github-user-search/start/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/github-user-search/start/public/logo192.png -------------------------------------------------------------------------------- /github-user-search/start/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/github-user-search/start/public/logo512.png -------------------------------------------------------------------------------- /github-user-search/start/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /github-user-search/start/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /github-user-search/start/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Dashboard, Login, PrivateRoute, AuthWrapper, Error } from './pages'; 3 | import { BrowserRouter, Routes, Route } from 'react-router-dom'; 4 | 5 | function App() { 6 | return ( 7 | 8 | 9 | }> 10 | }> 11 |
    }> 12 | 13 | 14 | ); 15 | } 16 | 17 | export default App; 18 | -------------------------------------------------------------------------------- /github-user-search/start/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | import Error from './pages/Error'; 4 | 5 | test('404 page back-home button goes to correct url', () => { 6 | const { getByTestId } = render(); 7 | const button = getByTestId('back-home'); 8 | expect(button.href).toContain('http://localhost'); 9 | }); 10 | -------------------------------------------------------------------------------- /github-user-search/start/src/components/Navbar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import { useAuth0 } from '@auth0/auth0-react'; 4 | 5 | const Navbar = () => { 6 | return

    navbar component

    ; 7 | }; 8 | 9 | const Wrapper = styled.nav` 10 | padding: 1.5rem; 11 | margin-bottom: 4rem; 12 | background: var(--clr-white); 13 | text-align: center; 14 | display: grid; 15 | grid-template-columns: auto auto 100px; 16 | justify-content: center; 17 | align-items: center; 18 | gap: 1.5rem; 19 | h4 { 20 | margin-bottom: 0; 21 | font-weight: 400; 22 | } 23 | img { 24 | width: 35px !important; 25 | height: 35px; 26 | border-radius: 50%; 27 | object-fit: cover; 28 | } 29 | button { 30 | background: transparent; 31 | border: transparent; 32 | font-size: 1.2rem; 33 | text-transform: capitalize; 34 | letter-spacing: var(--spacing); 35 | color: var(--clr-grey-5); 36 | cursor: pointer; 37 | } 38 | `; 39 | 40 | export default Navbar; 41 | -------------------------------------------------------------------------------- /github-user-search/start/src/components/Repos.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import { GithubContext } from '../context/context'; 4 | const Repos = () => { 5 | return

    repos component

    ; 6 | }; 7 | 8 | const Wrapper = styled.div` 9 | display: grid; 10 | justify-items: center; 11 | gap: 2rem; 12 | @media (min-width: 800px) { 13 | grid-template-columns: 1fr 1fr; 14 | } 15 | 16 | @media (min-width: 1200px) { 17 | grid-template-columns: 2fr 3fr; 18 | } 19 | 20 | div { 21 | width: 100% !important; 22 | } 23 | .fusioncharts-container { 24 | width: 100% !important; 25 | } 26 | svg { 27 | width: 100% !important; 28 | border-radius: var(--radius) !important; 29 | } 30 | `; 31 | 32 | export default Repos; 33 | -------------------------------------------------------------------------------- /github-user-search/start/src/components/User.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import Card from './Card'; 4 | import Followers from './Followers'; 5 | const User = () => { 6 | return

    search component

    ; 7 | }; 8 | 9 | const Wrapper = styled.div` 10 | padding-top: 2rem; 11 | display: grid; 12 | gap: 3rem 2rem; 13 | @media (min-width: 992px) { 14 | grid-template-columns: 1fr 1fr; 15 | } 16 | /* align-items: start; */ 17 | `; 18 | 19 | export default User; 20 | -------------------------------------------------------------------------------- /github-user-search/start/src/components/index.js: -------------------------------------------------------------------------------- 1 | import Info from "./Info"; 2 | import Repos from "./Repos"; 3 | import User from "./User"; 4 | import Search from "./Search"; 5 | import Navbar from "./Navbar"; 6 | 7 | export { Info, Repos, User, Search, Navbar }; 8 | -------------------------------------------------------------------------------- /github-user-search/start/src/context/context.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import mockUser from './mockData.js/mockUser'; 3 | import mockRepos from './mockData.js/mockRepos'; 4 | import mockFollowers from './mockData.js/mockFollowers'; 5 | import axios from 'axios'; 6 | 7 | const rootUrl = 'https://api.github.com'; 8 | 9 | const GithubContext = React.createContext(); 10 | 11 | const GithubProvider = ({ children }) => { 12 | return ( 13 | {children} 14 | ); 15 | }; 16 | 17 | export { GithubProvider, GithubContext }; 18 | -------------------------------------------------------------------------------- /github-user-search/start/src/context/mockData.js/mockUser.js: -------------------------------------------------------------------------------- 1 | export default { 2 | login: 'john-smilga', 3 | id: 42133389, 4 | node_id: 'MDQ6VXNlcjQyMTMzMzg5', 5 | avatar_url: 'https://avatars3.githubusercontent.com/u/42133389?v=4', 6 | gravatar_id: '', 7 | url: 'https://api.github.com/users/john-smilga', 8 | html_url: 'https://github.com/john-smilga', 9 | followers_url: 'https://api.github.com/users/john-smilga/followers', 10 | following_url: 11 | 'https://api.github.com/users/john-smilga/following{/other_user}', 12 | gists_url: 'https://api.github.com/users/john-smilga/gists{/gist_id}', 13 | starred_url: 14 | 'https://api.github.com/users/john-smilga/starred{/owner}{/repo}', 15 | subscriptions_url: 'https://api.github.com/users/john-smilga/subscriptions', 16 | organizations_url: 'https://api.github.com/users/john-smilga/orgs', 17 | repos_url: 'https://api.github.com/users/john-smilga/repos', 18 | events_url: 'https://api.github.com/users/john-smilga/events{/privacy}', 19 | received_events_url: 20 | 'https://api.github.com/users/john-smilga/received_events', 21 | type: 'User', 22 | site_admin: false, 23 | name: 'John Smilga', 24 | company: 'Coding Addict', 25 | blog: 'www.johnsmilga.com', 26 | location: 'Los Angeles', 27 | email: null, 28 | hireable: null, 29 | bio: 'Creator of Coding Addict', 30 | twitter_username: 'john_smilga', 31 | public_repos: 152, 32 | public_gists: 0, 33 | followers: 1495, 34 | following: 0, 35 | created_at: '2018-08-06T06:48:23Z', 36 | updated_at: '2020-07-08T05:01:32Z', 37 | }; 38 | -------------------------------------------------------------------------------- /github-user-search/start/src/images/preloader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/github-user-search/start/src/images/preloader.gif -------------------------------------------------------------------------------- /github-user-search/start/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | import { GithubProvider } from './context/context'; 7 | import { Auth0Provider } from '@auth0/auth0-react'; 8 | 9 | ReactDOM.render( 10 | 11 | 12 | 13 | 14 | , 15 | document.getElementById('root') 16 | ); 17 | 18 | // If you want your app to work offline and load faster, you can change 19 | // unregister() to register() below. Note this comes with some pitfalls. 20 | // Learn more about service workers: https://bit.ly/CRA-PWA 21 | serviceWorker.unregister(); 22 | -------------------------------------------------------------------------------- /github-user-search/start/src/pages/AuthWrapper.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useAuth0 } from '@auth0/auth0-react'; 3 | import loadingGif from '../images/preloader.gif'; 4 | import styled from 'styled-components'; 5 | function AuthWrapper() { 6 | return

    authwrapper component

    ; 7 | } 8 | 9 | const Wrapper = styled.section` 10 | min-height: 100vh; 11 | display: grid; 12 | place-items: center; 13 | img { 14 | width: 150px; 15 | } 16 | `; 17 | 18 | export default AuthWrapper; 19 | -------------------------------------------------------------------------------- /github-user-search/start/src/pages/Dashboard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Info, User, Search, Navbar } from '../components'; 3 | import loadingImage from '../images/preloader.gif'; 4 | import { GithubContext } from '../context/context'; 5 | const Dashboard = () => { 6 | return ( 7 |
    8 | 9 | 10 | 11 | 12 |
    13 | ); 14 | }; 15 | 16 | export default Dashboard; 17 | -------------------------------------------------------------------------------- /github-user-search/start/src/pages/Error.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import { Link } from 'react-router-dom'; 4 | const Error = () => { 5 | return ( 6 | 7 |

    404

    8 |

    Sorry, page not found

    9 | 10 | back home 11 | 12 |
    13 | ); 14 | }; 15 | const Wrapper = styled.section` 16 | min-height: 100vh; 17 | display: grid; 18 | place-items: center; 19 | background: var(--clr-primary-10); 20 | text-align: center; 21 | h1 { 22 | font-size: 10rem; 23 | } 24 | h3 { 25 | color: var(--clr-grey-3); 26 | margin-bottom: 1.5rem; 27 | } 28 | `; 29 | export default Error; 30 | -------------------------------------------------------------------------------- /github-user-search/start/src/pages/Login.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useAuth0 } from '@auth0/auth0-react'; 3 | import styled from 'styled-components'; 4 | import loginImg from '../images/login-img.svg'; 5 | const Login = () => { 6 | return ( 7 | 8 |
    9 | github-user 10 |

    github user

    11 | 12 |
    13 |
    14 | ); 15 | }; 16 | const Wrapper = styled.section` 17 | min-height: 100vh; 18 | display: grid; 19 | place-items: center; 20 | .container { 21 | width: 90vw; 22 | max-width: 600px; 23 | text-align: center; 24 | } 25 | img { 26 | margin-bottom: 2rem; 27 | } 28 | h1 { 29 | margin-bottom: 1.5rem; 30 | } 31 | `; 32 | export default Login; 33 | -------------------------------------------------------------------------------- /github-user-search/start/src/pages/PrivateRoute.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Route, Redirect } from 'react-router-dom'; 3 | import { useAuth0 } from '@auth0/auth0-react'; 4 | 5 | const PrivateRoute = () => { 6 | return

    private route component

    ; 7 | }; 8 | export default PrivateRoute; 9 | -------------------------------------------------------------------------------- /github-user-search/start/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import Dashboard from "./Dashboard"; 2 | import Login from "./Login"; 3 | import AuthWrapper from "./AuthWrapper"; 4 | import PrivateRoute from "./PrivateRoute"; 5 | import Error from "./Error"; 6 | 7 | export { Dashboard, Login, AuthWrapper, PrivateRoute, Error }; 8 | -------------------------------------------------------------------------------- /github-user-search/start/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | -------------------------------------------------------------------------------- /github-user-search/start/test/specs/functional.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | describe('Functional test', () => { 3 | it('pages render correctly', async () => { 4 | await browser.url(''); 5 | //in all snapshots, we will ignore these locators 6 | // await browser.execute('/*@visual.init*/', 'Visual Test'); 7 | // await browser.execute('/*@visual.snapshot*/', 'Dashboard Page'); 8 | 9 | await browser.url('/login'); 10 | // await browser.execute('/*@visual.snapshot*/', 'Login Page'); 11 | 12 | await browser.url('/foobar'); 13 | //await browser.debug(); 14 | // await browser.execute('sauce: break'); 15 | const button = await $('[data-testid="back-home"]'); 16 | await button.waitForDisplayed(); 17 | // await browser.execute('/*@visual.snapshot*/', 'Error Page'); 18 | 19 | const result = await browser.execute('/*@visual.end*/'); 20 | // expect(result.message).toBeNull(); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /github-user-search/start/test/specs/visual.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | describe('Visual checks', () => { 3 | it('pages render correctly', async () => { 4 | await browser.url(''); 5 | await browser.execute('/*@visual.init*/', 'Visual Test'); 6 | await browser.execute('/*@visual.snapshot*/', 'Dashboard Page'); 7 | 8 | await browser.url('/login'); 9 | await browser.execute('/*@visual.snapshot*/', 'Login Page'); 10 | 11 | await browser.url('/foobar'); 12 | //await browser.debug(); 13 | // await browser.execute('sauce: break'); 14 | const button = await $('[data-testid="back-home"]'); 15 | await button.waitForDisplayed(); 16 | await browser.execute('/*@visual.snapshot*/', 'Error Page'); 17 | 18 | const result = await browser.execute('/*@visual.end*/'); 19 | expect(result.message).toBeNull(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /react-tutorial/react-hooks/hooks-11-http-reducer/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /storybook/README.md: -------------------------------------------------------------------------------- 1 | # Testing With Storybook and Screener 2 | 3 | 1. Follow instructions to setup the project [here](./solution/taskbox/README.md) 4 | 1. Build your first components by following [this](https://www.learnstorybook.com/intro-to-storybook/react/en/simple-component/) 5 | 1. Then follow instructions to get started with [Screener Components](https://screener.io/v2/docs) 6 | 7 | ## Assemble a composite component 8 | 9 | A composite component is one that combines several components together. 10 | Follow [these](https://www.learnstorybook.com/intro-to-storybook/react/en/composite-component/) 11 | instructions to make this happen. 12 | -------------------------------------------------------------------------------- /storybook/solution/taskbox/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | .eslintcache 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | -------------------------------------------------------------------------------- /storybook/solution/taskbox/.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | //👇 Location of our stories 3 | stories: ['../src/components/**/*.stories.js'], 4 | addons: [ 5 | '@storybook/addon-links', 6 | '@storybook/addon-essentials', 7 | '@storybook/preset-create-react-app', 8 | ], 9 | }; -------------------------------------------------------------------------------- /storybook/solution/taskbox/.storybook/preview.js: -------------------------------------------------------------------------------- 1 | import '../src/index.css'; //👈 The app's CSS file goes here 2 | 3 | //👇 Configures Storybook to log the actions( onArchiveTask and onPinTask ) in the UI. 4 | export const parameters = { 5 | actions: { argTypesRegex: '^on[A-Z].*' }, 6 | }; -------------------------------------------------------------------------------- /storybook/solution/taskbox/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Chroma Software Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /storybook/solution/taskbox/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/storybook/solution/taskbox/public/favicon.ico -------------------------------------------------------------------------------- /storybook/solution/taskbox/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/storybook/solution/taskbox/public/logo192.png -------------------------------------------------------------------------------- /storybook/solution/taskbox/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/storybook/solution/taskbox/public/logo512.png -------------------------------------------------------------------------------- /storybook/solution/taskbox/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /storybook/solution/taskbox/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /storybook/solution/taskbox/screener.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | projectRepo: 'nadvolod/js-code', 3 | storybookConfigDir: '.storybook', 4 | storybookStaticDir: 'public', 5 | apiKey: process.env.SCREENER_API_KEY, 6 | //allows for automatic acceptance of components from this branch when merged to/from 7 | baseBranch: 'main', 8 | //This will run the components cross platform 9 | resolutions: [ 10 | { 11 | deviceName: 'iPhone X' 12 | }, 13 | { 14 | deviceName: 'iPhone X', 15 | deviceOrientation: 'landscape' 16 | }, 17 | { 18 | deviceName: 'Galaxy S8', 19 | deviceOrientation: 'landscape' 20 | }, 21 | ] 22 | }; -------------------------------------------------------------------------------- /storybook/solution/taskbox/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /storybook/solution/taskbox/src/App.js: -------------------------------------------------------------------------------- 1 | import logo from './logo.svg'; 2 | import './App.css'; 3 | 4 | function App() { 5 | return ( 6 |
    7 |
    8 | logo 9 |

    10 | Edit src/App.js and save to reload. 11 |

    12 | 18 | Learn React 19 | 20 |
    21 |
    22 | ); 23 | } 24 | 25 | export default App; 26 | -------------------------------------------------------------------------------- /storybook/solution/taskbox/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /storybook/solution/taskbox/src/assets/font/OpenSans-Light-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/storybook/solution/taskbox/src/assets/font/OpenSans-Light-webfont.eot -------------------------------------------------------------------------------- /storybook/solution/taskbox/src/assets/font/OpenSans-Light-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/storybook/solution/taskbox/src/assets/font/OpenSans-Light-webfont.ttf -------------------------------------------------------------------------------- /storybook/solution/taskbox/src/assets/font/OpenSans-Light-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/storybook/solution/taskbox/src/assets/font/OpenSans-Light-webfont.woff -------------------------------------------------------------------------------- /storybook/solution/taskbox/src/assets/font/OpenSans-Regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/storybook/solution/taskbox/src/assets/font/OpenSans-Regular-webfont.eot -------------------------------------------------------------------------------- /storybook/solution/taskbox/src/assets/font/OpenSans-Regular-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/storybook/solution/taskbox/src/assets/font/OpenSans-Regular-webfont.ttf -------------------------------------------------------------------------------- /storybook/solution/taskbox/src/assets/font/OpenSans-Regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/storybook/solution/taskbox/src/assets/font/OpenSans-Regular-webfont.woff -------------------------------------------------------------------------------- /storybook/solution/taskbox/src/assets/icon/percolate.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/storybook/solution/taskbox/src/assets/icon/percolate.eot -------------------------------------------------------------------------------- /storybook/solution/taskbox/src/assets/icon/percolate.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/storybook/solution/taskbox/src/assets/icon/percolate.ttf -------------------------------------------------------------------------------- /storybook/solution/taskbox/src/assets/icon/percolate.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/storybook/solution/taskbox/src/assets/icon/percolate.woff -------------------------------------------------------------------------------- /storybook/solution/taskbox/src/assets/icon/todos.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/storybook/solution/taskbox/src/assets/icon/todos.eot -------------------------------------------------------------------------------- /storybook/solution/taskbox/src/assets/icon/todos.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/storybook/solution/taskbox/src/assets/icon/todos.ttf -------------------------------------------------------------------------------- /storybook/solution/taskbox/src/assets/icon/todos.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/storybook/solution/taskbox/src/assets/icon/todos.woff -------------------------------------------------------------------------------- /storybook/solution/taskbox/src/components/Task.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | export default function Task({ task: { id, title, state }, onArchiveTask, onPinTask }) { 5 | return ( 6 |
    7 | 16 |
    17 | 18 |
    19 | 20 |
    event.stopPropagation()}> 21 | {state !== 'TASK_ARCHIVED' && ( 22 | // eslint-disable-next-line jsx-a11y/anchor-is-valid 23 | onPinTask(id)}> 24 | 25 | 26 | )} 27 |
    28 |
    29 | ); 30 | } 31 | 32 | Task.propTypes = { 33 | /** Composition of the task */ 34 | task: PropTypes.shape({ 35 | /** Id of the task */ 36 | id: PropTypes.string.isRequired, 37 | /** Title of the task */ 38 | title: PropTypes.string.isRequired, 39 | /** Current state of the task */ 40 | state: PropTypes.string.isRequired, 41 | }), 42 | /** Event to change the task to archived */ 43 | onArchiveTask: PropTypes.func, 44 | /** Event to change the task to pinned */ 45 | onPinTask: PropTypes.func, 46 | }; -------------------------------------------------------------------------------- /storybook/solution/taskbox/src/components/Task.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Task from './Task'; 4 | 5 | export default { 6 | component: Task, 7 | title: 'Task', 8 | }; 9 | 10 | const Template = args => ; 11 | 12 | export const Default = Template.bind({}); 13 | Default.args = { 14 | task: { 15 | id: '1', 16 | title: 'Test Task', 17 | state: 'TASK_INBOX', 18 | updatedAt: new Date(2018, 0, 1, 9, 0), 19 | }, 20 | }; 21 | 22 | export const Pinned = Template.bind({}); 23 | Pinned.args = { 24 | task: { 25 | ...Default.args.task, 26 | state: 'TASK_PINNED', 27 | }, 28 | }; 29 | 30 | export const Archived = Template.bind({}); 31 | Archived.args = { 32 | task: { 33 | ...Default.args.task, 34 | state: 'TASK_ARCHIVED', 35 | }, 36 | }; -------------------------------------------------------------------------------- /storybook/solution/taskbox/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /storybook/solution/taskbox/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /storybook/solution/taskbox/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /storybook/solution/taskbox/src/stories/Button.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import './button.css'; 4 | 5 | /** 6 | * Primary UI component for user interaction 7 | */ 8 | export const Button = ({ primary, backgroundColor, size, label, ...props }) => { 9 | const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary'; 10 | return ( 11 | 19 | ); 20 | }; 21 | 22 | Button.propTypes = { 23 | /** 24 | * Is this the principal call to action on the page? 25 | */ 26 | primary: PropTypes.bool, 27 | /** 28 | * What background color to use 29 | */ 30 | backgroundColor: PropTypes.string, 31 | /** 32 | * How large should the button be? 33 | */ 34 | size: PropTypes.oneOf(['small', 'medium', 'large']), 35 | /** 36 | * Button contents 37 | */ 38 | label: PropTypes.string.isRequired, 39 | /** 40 | * Optional click handler 41 | */ 42 | onClick: PropTypes.func, 43 | }; 44 | 45 | Button.defaultProps = { 46 | backgroundColor: null, 47 | primary: false, 48 | size: 'medium', 49 | onClick: undefined, 50 | }; 51 | -------------------------------------------------------------------------------- /storybook/solution/taskbox/src/stories/Button.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Button } from './Button'; 4 | 5 | export default { 6 | title: 'Example/Button', 7 | component: Button, 8 | argTypes: { 9 | backgroundColor: { control: 'color' }, 10 | }, 11 | }; 12 | 13 | const Template = (args) => 20 | 28 | onChange={(e) => setDisabled(e.target.checked)} 29 | /> 30 | 31 | 32 | 33 |
    34 | ); 35 | } 36 | 37 | export default App; 38 | -------------------------------------------------------------------------------- /testing-js/react-components/test-app/src/Button.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen, fireEvent } from '@testing-library/react'; 2 | import App from './App'; 3 | import Button from './components/function-component/Button'; 4 | 5 | test('should start in red state', () => { 6 | render( 25 |

    Now you see me!

    26 | 27 | ); 28 | } else { 29 | return ( 30 |
    31 | 32 |
    33 | ); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /testing-js/react-components/test-app/src/components/function-component/Button.js: -------------------------------------------------------------------------------- 1 | // https://reactjs.org/docs/testing-recipes.html#rendering 2 | import React, { useState } from "react"; 3 | 4 | export default function Button(props) { 5 | const [buttonColor, setButtonColor] = useState('red'); 6 | const newButtonColor = buttonColor === 'red' ? 'blue' : 'red'; 7 | return ( 8 | 13 | ); 14 | } -------------------------------------------------------------------------------- /testing-js/react-components/test-app/src/components/function-component/Hello.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function Hello(props) { 4 | if (props.name) { 5 | return

    Hello, {props.name}!

    ; 6 | } else { 7 | return Hey, stranger; 8 | } 9 | } -------------------------------------------------------------------------------- /testing-js/react-components/test-app/src/components/function-component/Toggle.js: -------------------------------------------------------------------------------- 1 | // https://reactjs.org/docs/testing-recipes.html#rendering 2 | import React, { useState } from "react"; 3 | 4 | export default function Toggle(props) { 5 | const [state, setState] = useState(false); 6 | return ( 7 | 16 | ); 17 | } -------------------------------------------------------------------------------- /testing-js/react-components/test-app/src/components/function-component/favorite-number.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | function FavoriteNumber({min = 1, max = 9}) { 4 | const [number, setNumber] = React.useState(0) 5 | const [numberEntered, setNumberEntered] = React.useState(false) 6 | function handleChange(event) { 7 | setNumber(Number(event.target.value)) 8 | setNumberEntered(true) 9 | } 10 | const isValid = !numberEntered || (number >= min && number <= max) 11 | return ( 12 |
    13 | 14 | 20 | {isValid ? null :
    The number is invalid
    } 21 |
    22 | ) 23 | } 24 | 25 | export {FavoriteNumber} -------------------------------------------------------------------------------- /testing-js/react-components/test-app/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /testing-js/react-components/test-app/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /testing-js/react-components/test-app/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /testing-js/react-components/test-app/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /web-testing-2022/.eslintignore: -------------------------------------------------------------------------------- 1 | .tmp/ 2 | **/node_modules/** 3 | **/client/dist/** -------------------------------------------------------------------------------- /web-testing-2022/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 saucelabs-training 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /web-testing-2022/README.md: -------------------------------------------------------------------------------- 1 | # web testing with javascript workshop 2 | 3 | ## [Moved to this repo](https://github.com/nadvolod/web-testing-22/blob/main/README.md) 4 | -------------------------------------------------------------------------------- /web-testing-2022/cypress.config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require('cypress'); 2 | 3 | module.exports = defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | baseUrl: 'http://localhost:3000', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /web-testing-2022/cypress/e2e/exercise.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe('Exercise', () => { 4 | it('loads', () => { 5 | /** Your code below */ 6 | 7 | //1. Use cy.visit('/') to go to the app url 8 | //2. Use cy.get('.App-link').should('be.visible') to assert valid state 9 | 10 | /** Your code above */ 11 | }); 12 | 13 | it('link goes to ultimateqa', () => { 14 | /** Your code below */ 15 | // Don't change this line, but make the test pass! 16 | cy.get('.App-link').should('have.attr', 'href').and('equal', 'https://www.ultimateqa.com'); 17 | /** Your code above */ 18 | }); 19 | 20 | it('link should open in a new tab', () => { 21 | /** Your code below */ 22 | 23 | /** Your code above */ 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /web-testing-2022/cypress/e2e/sanity.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe('Solution', () => { 4 | it('loads', () => { 5 | cy.visit('/'); 6 | cy.get('.App-link').should('be.visible'); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /web-testing-2022/cypress/e2e/solution.cy.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | describe('Solution', () => { 4 | it('loads', () => { 5 | cy.visit('/'); 6 | cy.get('.App-link').should('be.visible'); 7 | }); 8 | 9 | it('link goes to ultimateqa', () => { 10 | cy.visit('/'); 11 | cy.get('.App-link') 12 | .should('have.attr', 'href') 13 | .and('equal', 'https://www.ultimateqa.com'); 14 | }); 15 | 16 | it('should open link in new tab', () => { 17 | cy.visit('/'); 18 | cy.get('.App-link').should('have.attr', 'target').and('equal', '_blank'); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /web-testing-2022/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /web-testing-2022/cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add('login', (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This will overwrite an existing command -- 25 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) 26 | -------------------------------------------------------------------------------- /web-testing-2022/cypress/support/e2e.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/e2e.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands'; 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /web-testing-2022/docs/CICD.md: -------------------------------------------------------------------------------- 1 | # CICD 2 | 3 | ## 🏋️‍♀️Let's add this code to our CI system. 4 | 5 | 1. In the repository's Github, go to `Actions` tab 6 | 2. Configure `Node.js` 7 | 3. Keep the name of the file and on line 4, or change, up to you 8 | 4. Copy + Paste the code from [comprehensive.yml](../.github/workflows/comprehensive.yml) 9 | 10 | 5. Add New repository secrets for the repo 11 | 12 | 6. Commit the new file 13 | 7. `git push` all of your changes to your repo 14 | 15 | ## 🧪Current Test Coverage 16 | 17 | [Look here](TEST-COVERAGE.md) 18 | 19 | ## 📝Summary 20 | 21 | ✅ We can use Github Workflows for free and easy continuous integration pipelines 22 | 23 | [Happo.io cicd docs](https://docs.happo.io/docs/continuous-integration#github) 24 | 25 | ## ⏭️[Conclusions](./CONCLUSIONS.md) 26 | -------------------------------------------------------------------------------- /web-testing-2022/docs/TEST-COVERAGE.md: -------------------------------------------------------------------------------- 1 | ## 🧪Our Testing Strategy 2 | 3 | | Expected Behavior | Tested? | Test Type | Technologies | 4 | | ---------------------------------------------------------- | ------- | ----------------- | ------------ | 5 | | Application renders | 🙅‍♂️ | ui/e2e/functional | | 6 | | Link goes to correct location | 🙅‍♂️ | | | 7 | | Link opens in new tab | 🙅‍♂️ | | | 8 | | App looks as expected on Chrome on most popular resolution | 🙅‍♂️ | | | 9 | | App looks as expected on Safari on most popular resolution | 🙅‍♂️ | | | 10 | | App is accessibility friendly | 🙅‍♂️ | | | 11 | | Front-end performance is at least a B | 🙅‍♂️ | | | 12 | | App is secure | 🙅‍♂️ | | | 13 | | Multiple other testing types... | 🙅‍♂️ | | | 14 | -------------------------------------------------------------------------------- /web-testing-2022/docs/visual-testing.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/web-testing-2022/docs/visual-testing.pdf -------------------------------------------------------------------------------- /web-testing-2022/graphics/bye.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/web-testing-2022/graphics/bye.gif -------------------------------------------------------------------------------- /web-testing-2022/graphics/component-diagram.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/web-testing-2022/graphics/component-diagram.jpeg -------------------------------------------------------------------------------- /web-testing-2022/graphics/me-and-mia.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/web-testing-2022/graphics/me-and-mia.jpg -------------------------------------------------------------------------------- /web-testing-2022/graphics/secrets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/web-testing-2022/graphics/secrets.png -------------------------------------------------------------------------------- /web-testing-2022/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-testing-2022", 3 | "version": "1.0.0", 4 | "description": "[#testing4good](https://twitter.com/hashtag/Testing4Good)", 5 | "main": "index.js", 6 | "directories": { 7 | "doc": "docs" 8 | }, 9 | "dependencies": { 10 | "@testing-library/react": "^13.4.0", 11 | "@testing-library/user-event": "^13.5.0", 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0", 14 | "react-scripts": "5.0.1", 15 | "web-vitals": "^2.1.4" 16 | }, 17 | "devDependencies": { 18 | "cypress": "^10.4.0" 19 | }, 20 | "scripts": { 21 | "start": "react-scripts start", 22 | "build": "react-scripts build", 23 | "happo": "happo", 24 | "happo-ci-github-actions": "happo-ci-github-actions", 25 | "test": "react-scripts test", 26 | "eject": "react-scripts eject", 27 | "test:cy:ci": "npx cypress run --spec '**/**/solution.cy.js' --browser=chrome", 28 | "test:cy:sanity": "npx cypress run --spec '**/**/sanity.cy.js' --browser=chrome", 29 | "test:visual": "npx happo-e2e -- npx cypress run --spec 'cypress/e2e/visual.cy.js'", 30 | "test:sanity": "npm run test:cy:sanity && npm run test:visual" 31 | }, 32 | "eslintConfig": { 33 | "extends": [ 34 | "react-app", 35 | "react-app/jest" 36 | ] 37 | }, 38 | "browserslist": { 39 | "production": [ 40 | ">0.2%", 41 | "not dead", 42 | "not op_mini all" 43 | ], 44 | "development": [ 45 | "last 1 chrome version", 46 | "last 1 firefox version", 47 | "last 1 safari version" 48 | ] 49 | }, 50 | "keywords": [], 51 | "author": "", 52 | "license": "ISC" 53 | } 54 | -------------------------------------------------------------------------------- /web-testing-2022/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/web-testing-2022/public/favicon.ico -------------------------------------------------------------------------------- /web-testing-2022/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/web-testing-2022/public/logo192.png -------------------------------------------------------------------------------- /web-testing-2022/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nadvolod/js-code/1a528d6701aacee4d6098a05b81d5555c22eba79/web-testing-2022/public/logo512.png -------------------------------------------------------------------------------- /web-testing-2022/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /web-testing-2022/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /web-testing-2022/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /web-testing-2022/src/App.js: -------------------------------------------------------------------------------- 1 | import logo from './logo.svg'; 2 | import './App.css'; 3 | 4 | function App() { 5 | return ( 6 |
    7 |
    8 | logo 9 |

    10 | Edit src/App.js and save to reload. 11 |

    12 | 18 | Learn React 19 | 20 |
    21 |
    22 | ); 23 | } 24 | 25 | export default App; 26 | -------------------------------------------------------------------------------- /web-testing-2022/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /web-testing-2022/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /web-testing-2022/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './App'; 4 | import './index.css'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | const root = ReactDOM.createRoot(document.getElementById('root')); 8 | root.render( 9 | 10 | 11 | , 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /web-testing-2022/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /web-testing-2022/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | --------------------------------------------------------------------------------