diff --git a/.gitignore b/.gitignore index d216b6f9..2a684dd4 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,5 @@ testem.log .DS_Store Thumbs.db tmp.html +/local-samples +*.mp4 diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..ab3ed898 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,20 @@ + +language: node_js +node_js: + - 12 +addons: + apt: + packages: + # Ubuntu 16+ does not install this dependency by default, so we need to install it ourselves + - libgconf-2-4 +cache: + # Caches $HOME/.npm when npm ci is default script command + # Caches node_modules in all other cases + npm: true + directories: + # we also need to cache folder with Cypress binary + - ~/.cache +install: + - npm ci +script: + - npm run e2e diff --git a/README.md b/README.md index cf7a7937..2c3243cb 100644 --- a/README.md +++ b/README.md @@ -3,16 +3,14 @@ This repository contains the code of the [Angular Testing Course](https://angular-university.io/course/angular-testing-course). -This course repository is updated to Angular v7, and there is a package-lock.json file available, for avoiding semantic versioning installation issues. +This course repository is updated to Angular v8, and there is a package-lock.json file available, for avoiding semantic versioning installation issues. ![Angular Testing Course](https://s3-us-west-1.amazonaws.com/angular-university/course-images/angular-testing-small.png) # Installation pre-requisites -IMPORTANT: Please use NPM 5 or above, to make sure the package-lock.json is used. - -For running this project we need and npm installed on our machine. These are some tutorials to install node in different operating systems: +For running this project we need node and npm installed on our machine. These are some tutorials to install node in different operating systems: *Its important to install the latest version of Node* diff --git a/browserslist b/browserslist new file mode 100644 index 00000000..80848532 --- /dev/null +++ b/browserslist @@ -0,0 +1,12 @@ +# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. +# For additional information regarding the format and rule options, please see: +# https://github.com/browserslist/browserslist#queries + +# You can see what browsers were selected by your queries by running: +# npx browserslist + +> 0.5% +last 2 versions +Firefox ESR +not dead +not IE 9-11 # For IE 9-11 support, remove 'not'. \ No newline at end of file diff --git a/cypress.json b/cypress.json new file mode 100644 index 00000000..fce87bfb --- /dev/null +++ b/cypress.json @@ -0,0 +1,3 @@ +{ + "baseUrl": "http://localhost:4200" +} diff --git a/cypress/fixtures/courses.json b/cypress/fixtures/courses.json new file mode 100644 index 00000000..ea6a5243 --- /dev/null +++ b/cypress/fixtures/courses.json @@ -0,0 +1,152 @@ +{ + "payload": [ + { + "id": 1, + "titles": { + "description": "Serverless Angular with Firebase Course", + "longDescription": "Serveless Angular with Firestore, Firebase Storage & Hosting, Firebase Cloud Functions & AngularFire" + }, + "iconUrl": "https://s3-us-west-1.amazonaws.com/angular-university/course-images/serverless-angular-small.png", + "lessonsCount": 10, + "category": "BEGINNER", + "seqNo": 4, + "url": "serverless-angular" + }, + { + "id": 2, + "titles": { + "description": "Angular Core Deep Dive", + "longDescription": "A detailed walk-through of the most important part of Angular - the Core and Common modules" + }, + "iconUrl": "https://s3-us-west-1.amazonaws.com/angular-university/course-images/angular-core-in-depth-small.png", + "lessonsCount": 10, + "category": "BEGINNER", + "seqNo": 1, + "url": "angular-core-course" + }, + { + "id": 3, + "titles": { + "description": "RxJs In Practice Course", + "longDescription": "Understand the RxJs Observable pattern, learn the RxJs Operators via practical examples" + }, + "iconUrl": "https://s3-us-west-1.amazonaws.com/angular-university/course-images/rxjs-in-practice-course.png", + "courseListIcon": "https://angular-academy.s3.amazonaws.com/main-logo/main-page-logo-small-hat.png", + "category": "BEGINNER", + "lessonsCount": 10, + "seqNo": 2, + "url": "rxjs-course" + }, + { + "id": 4, + "titles": { + "description": "NgRx In Depth", + "longDescription": "Learn the modern Ngrx Ecosystem, including Store, Effects, Router Store, Ngrx Entity, Dev Tools and Schematics." + }, + "iconUrl": "https://s3-us-west-1.amazonaws.com/angular-university/course-images/angular-ngrx-course.png", + "courseListIcon": "https://angular-academy.s3.amazonaws.com/main-logo/main-page-logo-small-hat.png", + "category": "BEGINNER", + "lessonsCount": 10, + "seqNo": 3, + "url": "ngrx-course" + }, + { + "id": 5, + "titles": { + "description": "Angular for Beginners", + "longDescription": "Establish a solid layer of fundamentals, learn what's under the hood of Angular" + }, + "iconUrl": "https://angular-academy.s3.amazonaws.com/thumbnails/angular2-for-beginners-small-v2.png", + "courseListIcon": "https://angular-academy.s3.amazonaws.com/main-logo/main-page-logo-small-hat.png", + "category": "BEGINNER", + "lessonsCount": 10, + "seqNo": 5, + "url": "angular-for-beginners" + }, + { + "id": 6, + "titles": { + "description": "Angular Security Course - Web Security Fundamentals", + "longDescription": "Learn Web Security Fundamentals and apply them to defend an Angular / Node Application from multiple types of attacks." + }, + "iconUrl": "https://s3-us-west-1.amazonaws.com/angular-university/course-images/security-cover-small-v2.png", + "courseListIcon": "https://s3-us-west-1.amazonaws.com/angular-university/course-images/lock-v2.png", + "category": "ADVANCED", + "lessonsCount": 11, + "seqNo": 6, + "url": "angular-security-course" + }, + { + "id": 7, + "titles": { + "description": "Angular PWA - Progressive Web Apps Course", + "longDescription": "Learn Angular Progressive Web Applications, build the future of the Web Today." + }, + "iconUrl": "https://s3-us-west-1.amazonaws.com/angular-university/course-images/angular-pwa-course.png", + "courseListIcon": "https://s3-us-west-1.amazonaws.com/angular-university/course-images/alien.png", + "category": "ADVANCED", + "lessonsCount": 8, + "seqNo": 7, + "url": "angular-pwa-course" + }, + { + "id": 8, + "titles": { + "description": "Angular Advanced Library Laboratory: Build Your Own Library", + "longDescription": "Learn Advanced Angular functionality typically used in Library Development. Advanced Components, Directives, Testing, Npm" + }, + "iconUrl": "https://angular-academy.s3.amazonaws.com/thumbnails/advanced_angular-small-v3.png", + "courseListIcon": "https://angular-academy.s3.amazonaws.com/thumbnails/angular-advanced-lesson-icon.png", + "category": "ADVANCED", + "seqNo": 8, + "url": "angular-advanced-course" + }, + { + "id": 9, + "titles": { + "description": "The Complete Typescript Course", + "longDescription": "Complete Guide to Typescript From Scratch: Learn the language in-depth and use it to build a Node REST API." + }, + "iconUrl": "https://angular-academy.s3.amazonaws.com/thumbnails/typescript-2-small.png", + "courseListIcon": "https://angular-academy.s3.amazonaws.com/thumbnails/typescript-2-lesson.png", + "category": "BEGINNER", + "seqNo": 9, + "url": "typescript-course" + }, + { + "id": 10, + "titles": { + "description": "Rxjs and Reactive Patterns Angular Architecture Course", + "longDescription": "Learn the core RxJs Observable Pattern as well and many other Design Patterns for building Reactive Angular Applications." + }, + "iconUrl": "https://s3-us-west-1.amazonaws.com/angular-academy/blog/images/rxjs-reactive-patterns-small.png", + "courseListIcon": "https://angular-academy.s3.amazonaws.com/course-logos/observables_rxjs.png", + "category": "BEGINNER", + "seqNo": 10, + "url": "rxjs-patterns-course" + }, + { + "id": 11, + "titles": { + "description": "Angular Material Course", + "longDescription": "Build Applications with the official Angular Widget Library" + }, + "iconUrl": "https://s3-us-west-1.amazonaws.com/angular-university/course-images/material_design.png", + "category": "BEGINNER", + "seqNo": 11, + "url": "angular-material-course" + }, + { + "id": 12, + "titles": { + "description": "Angular Testing Course", + "longDescription": "In-depth guide to Unit Testing and E2E Testing of Angular Applications" + }, + "iconUrl": "https://s3-us-west-1.amazonaws.com/angular-university/course-images/angular-testing-small.png", + "category": "BEGINNER", + "seqNo": 0, + "url": "angular-testing-course", + "lessonsCount": 10 + } + ] +} diff --git a/cypress/fixtures/testing-course.json b/cypress/fixtures/testing-course.json new file mode 100644 index 00000000..fe9c9d2a --- /dev/null +++ b/cypress/fixtures/testing-course.json @@ -0,0 +1,12 @@ +{ + "id": 12, + "titles": { + "description": "Angular Testing Course", + "longDescription": "In-depth guide to Unit Testing and E2E Testing of Angular Applications" + }, + "iconUrl": "https://s3-us-west-1.amazonaws.com/angular-university/course-images/angular-testing-small.png", + "category": "BEGINNER", + "seqNo": 0, + "url": "angular-testing-course", + "lessonsCount": 10 +} diff --git a/cypress/fixtures/testing-lessons-page-1.json b/cypress/fixtures/testing-lessons-page-1.json new file mode 100644 index 00000000..bab1cd91 --- /dev/null +++ b/cypress/fixtures/testing-lessons-page-1.json @@ -0,0 +1,25 @@ +{ + "payload": [ + { + "id": 40, + "description": "Angular Testing Course - Helicopter View", + "duration": "5:38", + "seqNo": 1, + "courseId": 12 + }, + { + "id": 41, + "description": "Setting Up the Development Environment", + "duration": "5:12", + "seqNo": 2, + "courseId": 12 + }, + { + "id": 42, + "description": "Introduction to Jasmine, Spies and specs", + "duration": "4:07", + "seqNo": 3, + "courseId": 12 + } + ] +} diff --git a/cypress/fixtures/testing-lessons-page-2.json b/cypress/fixtures/testing-lessons-page-2.json new file mode 100644 index 00000000..38dcd78b --- /dev/null +++ b/cypress/fixtures/testing-lessons-page-2.json @@ -0,0 +1,25 @@ +{ + "payload": [ + { + "id": 43, + "description": "Introduction to Service Testing", + "duration": "7:32", + "seqNo": 4, + "courseId": 12 + }, + { + "id": 44, + "description": "Settting up the Angular TestBed", + "duration": "6:28", + "seqNo": 5, + "courseId": 12 + }, + { + "id": 45, + "description": "Mocking Angular HTTP requests", + "duration": "4:38", + "seqNo": 6, + "courseId": 12 + } + ] +} diff --git a/cypress/integration/examples/actions.spec.js b/cypress/integration/examples/actions.spec.js new file mode 100644 index 00000000..8923d99d --- /dev/null +++ b/cypress/integration/examples/actions.spec.js @@ -0,0 +1,281 @@ +/// + +context('Actions', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/commands/actions') + }) + + // https://on.cypress.io/interacting-with-elements + + it('.type() - type into a DOM element', () => { + // https://on.cypress.io/type + cy.get('.action-email') + .type('fake@email.com').should('have.value', 'fake@email.com') + + // .type() with special character sequences + .type('{leftarrow}{rightarrow}{uparrow}{downarrow}') + .type('{del}{selectall}{backspace}') + + // .type() with key modifiers + .type('{alt}{option}') //these are equivalent + .type('{ctrl}{control}') //these are equivalent + .type('{meta}{command}{cmd}') //these are equivalent + .type('{shift}') + + // Delay each keypress by 0.1 sec + .type('slow.typing@email.com', { delay: 100 }) + .should('have.value', 'slow.typing@email.com') + + cy.get('.action-disabled') + // Ignore error checking prior to type + // like whether the input is visible or disabled + .type('disabled error checking', { force: true }) + .should('have.value', 'disabled error checking') + }) + + it('.focus() - focus on a DOM element', () => { + // https://on.cypress.io/focus + cy.get('.action-focus').focus() + .should('have.class', 'focus') + .prev().should('have.attr', 'style', 'color: orange;') + }) + + it('.blur() - blur off a DOM element', () => { + // https://on.cypress.io/blur + cy.get('.action-blur').type('About to blur').blur() + .should('have.class', 'error') + .prev().should('have.attr', 'style', 'color: red;') + }) + + it('.clear() - clears an input or textarea element', () => { + // https://on.cypress.io/clear + cy.get('.action-clear').type('Clear this text') + .should('have.value', 'Clear this text') + .clear() + .should('have.value', '') + }) + + it('.submit() - submit a form', () => { + // https://on.cypress.io/submit + cy.get('.action-form') + .find('[type="text"]').type('HALFOFF') + cy.get('.action-form').submit() + .next().should('contain', 'Your form has been submitted!') + }) + + it('.click() - click on a DOM element', () => { + // https://on.cypress.io/click + cy.get('.action-btn').click() + + // You can click on 9 specific positions of an element: + // ----------------------------------- + // | topLeft top topRight | + // | | + // | | + // | | + // | left center right | + // | | + // | | + // | | + // | bottomLeft bottom bottomRight | + // ----------------------------------- + + // clicking in the center of the element is the default + cy.get('#action-canvas').click() + + cy.get('#action-canvas').click('topLeft') + cy.get('#action-canvas').click('top') + cy.get('#action-canvas').click('topRight') + cy.get('#action-canvas').click('left') + cy.get('#action-canvas').click('right') + cy.get('#action-canvas').click('bottomLeft') + cy.get('#action-canvas').click('bottom') + cy.get('#action-canvas').click('bottomRight') + + // .click() accepts an x and y coordinate + // that controls where the click occurs :) + + cy.get('#action-canvas') + .click(80, 75) // click 80px on x coord and 75px on y coord + .click(170, 75) + .click(80, 165) + .click(100, 185) + .click(125, 190) + .click(150, 185) + .click(170, 165) + + // click multiple elements by passing multiple: true + cy.get('.action-labels>.label').click({ multiple: true }) + + // Ignore error checking prior to clicking + cy.get('.action-opacity>.btn').click({ force: true }) + }) + + it('.dblclick() - double click on a DOM element', () => { + // https://on.cypress.io/dblclick + + // Our app has a listener on 'dblclick' event in our 'scripts.js' + // that hides the div and shows an input on double click + cy.get('.action-div').dblclick().should('not.be.visible') + cy.get('.action-input-hidden').should('be.visible') + }) + + it('.rightclick() - right click on a DOM element', () => { + // https://on.cypress.io/rightclick + + // Our app has a listener on 'contextmenu' event in our 'scripts.js' + // that hides the div and shows an input on right click + cy.get('.rightclick-action-div').rightclick().should('not.be.visible') + cy.get('.rightclick-action-input-hidden').should('be.visible') + }) + + it('.check() - check a checkbox or radio element', () => { + // https://on.cypress.io/check + + // By default, .check() will check all + // matching checkbox or radio elements in succession, one after another + cy.get('.action-checkboxes [type="checkbox"]').not('[disabled]') + .check().should('be.checked') + + cy.get('.action-radios [type="radio"]').not('[disabled]') + .check().should('be.checked') + + // .check() accepts a value argument + cy.get('.action-radios [type="radio"]') + .check('radio1').should('be.checked') + + // .check() accepts an array of values + cy.get('.action-multiple-checkboxes [type="checkbox"]') + .check(['checkbox1', 'checkbox2']).should('be.checked') + + // Ignore error checking prior to checking + cy.get('.action-checkboxes [disabled]') + .check({ force: true }).should('be.checked') + + cy.get('.action-radios [type="radio"]') + .check('radio3', { force: true }).should('be.checked') + }) + + it('.uncheck() - uncheck a checkbox element', () => { + // https://on.cypress.io/uncheck + + // By default, .uncheck() will uncheck all matching + // checkbox elements in succession, one after another + cy.get('.action-check [type="checkbox"]') + .not('[disabled]') + .uncheck().should('not.be.checked') + + // .uncheck() accepts a value argument + cy.get('.action-check [type="checkbox"]') + .check('checkbox1') + .uncheck('checkbox1').should('not.be.checked') + + // .uncheck() accepts an array of values + cy.get('.action-check [type="checkbox"]') + .check(['checkbox1', 'checkbox3']) + .uncheck(['checkbox1', 'checkbox3']).should('not.be.checked') + + // Ignore error checking prior to unchecking + cy.get('.action-check [disabled]') + .uncheck({ force: true }).should('not.be.checked') + }) + + it('.select() - select an option in a