|
@@ -108,13 +108,60 @@
|
|
| 108 |
"src/**/*.html"
|
| 109 |
]
|
| 110 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
}
|
| 112 |
}
|
| 113 |
}
|
| 114 |
},
|
| 115 |
"cli": {
|
| 116 |
"schematicCollections": [
|
| 117 |
-
"
|
|
|
|
|
|
|
| 118 |
]
|
| 119 |
}
|
| 120 |
-
}
|
| 108 |
"src/**/*.html"
|
| 109 |
]
|
| 110 |
}
|
| 111 |
+
},
|
| 112 |
+
"cypress-run": {
|
| 113 |
+
"builder": "@cypress/schematic:cypress",
|
| 114 |
+
"options": {
|
| 115 |
+
"devServerTarget": "book-monkey:serve"
|
| 116 |
+
},
|
| 117 |
+
"configurations": {
|
| 118 |
+
"production": {
|
| 119 |
+
"devServerTarget": "book-monkey:serve:production"
|
| 120 |
+
}
|
| 121 |
+
}
|
| 122 |
+
},
|
| 123 |
+
"cypress-open": {
|
| 124 |
+
"builder": "@cypress/schematic:cypress",
|
| 125 |
+
"options": {
|
| 126 |
+
"watch": true,
|
| 127 |
+
"headless": false
|
| 128 |
+
}
|
| 129 |
+
},
|
| 130 |
+
"ct": {
|
| 131 |
+
"builder": "@cypress/schematic:cypress",
|
| 132 |
+
"options": {
|
| 133 |
+
"devServerTarget": "book-monkey:serve",
|
| 134 |
+
"watch": true,
|
| 135 |
+
"headless": false,
|
| 136 |
+
"testingType": "component"
|
| 137 |
+
},
|
| 138 |
+
"configurations": {
|
| 139 |
+
"development": {
|
| 140 |
+
"devServerTarget": "book-monkey:serve:development"
|
| 141 |
+
}
|
| 142 |
+
}
|
| 143 |
+
},
|
| 144 |
+
"e2e": {
|
| 145 |
+
"builder": "@cypress/schematic:cypress",
|
| 146 |
+
"options": {
|
| 147 |
+
"devServerTarget": "book-monkey:serve",
|
| 148 |
+
"watch": true,
|
| 149 |
+
"headless": false
|
| 150 |
+
},
|
| 151 |
+
"configurations": {
|
| 152 |
+
"production": {
|
| 153 |
+
"devServerTarget": "book-monkey:serve:production"
|
| 154 |
+
}
|
| 155 |
+
}
|
| 156 |
}
|
| 157 |
}
|
| 158 |
}
|
| 159 |
},
|
| 160 |
"cli": {
|
| 161 |
"schematicCollections": [
|
| 162 |
+
"@cypress/schematic",
|
| 163 |
+
"angular-eslint",
|
| 164 |
+
"@schematics/angular"
|
| 165 |
]
|
| 166 |
}
|
| 167 |
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
describe('BookMonkey', () => {
|
| 2 |
+
beforeEach(() => {
|
| 3 |
+
cy.visit('/');
|
| 4 |
+
});
|
| 5 |
+
|
| 6 |
+
it('should open the home page by default', () => {
|
| 7 |
+
cy.get('h1')
|
| 8 |
+
.should('contain', 'Home');
|
| 9 |
+
cy.url()
|
| 10 |
+
.should('contain', '/home');
|
| 11 |
+
});
|
| 12 |
+
|
| 13 |
+
it('should not show the administration form when not logged in', () => {
|
| 14 |
+
cy.get('nav button')
|
| 15 |
+
.as('loginLogoutBtn')
|
| 16 |
+
.contains('Logout')
|
| 17 |
+
.should('have.class', 'red')
|
| 18 |
+
.click();
|
| 19 |
+
cy.get('@loginLogoutBtn')
|
| 20 |
+
.contains('Login')
|
| 21 |
+
.should('have.class', 'green');
|
| 22 |
+
cy.get('nav')
|
| 23 |
+
.contains('Administration')
|
| 24 |
+
.as('adminButton')
|
| 25 |
+
.click();
|
| 26 |
+
cy.on('window:alert', (str) => {
|
| 27 |
+
expect(str).to.equal('Not logged in!');
|
| 28 |
+
});
|
| 29 |
+
cy.url()
|
| 30 |
+
.should('not.contain', '/admin/create');
|
| 31 |
+
cy.get('@adminButton')
|
| 32 |
+
.should('not.have.class', 'active');
|
| 33 |
+
});
|
| 34 |
+
|
| 35 |
+
it('should find all angular books using search input', () => {
|
| 36 |
+
cy.intercept('GET', 'https://api5.angular-buch.com/books/search/Angular')
|
| 37 |
+
.as('search');
|
| 38 |
+
cy.get('input[type=search]')
|
| 39 |
+
.clear()
|
| 40 |
+
.type('Angular');
|
| 41 |
+
cy.wait('@search')
|
| 42 |
+
.its('response.statusCode')
|
| 43 |
+
.should('eq', 200);
|
| 44 |
+
cy.get('.search-results')
|
| 45 |
+
.find('li')
|
| 46 |
+
.should('have.length.gte', 3)
|
| 47 |
+
.each(($li) => {
|
| 48 |
+
cy.wrap($li).contains('Angular')
|
| 49 |
+
});
|
| 50 |
+
});
|
| 51 |
+
|
| 52 |
+
it('should not open the results box on server errors', () => {
|
| 53 |
+
cy.intercept('GET', 'https://api5.angular-buch.com/books/search/*', {
|
| 54 |
+
statusCode: 500,
|
| 55 |
+
body: '500 Internal Server Error',
|
| 56 |
+
}).as('search');
|
| 57 |
+
cy.get('input[type=search]')
|
| 58 |
+
.clear()
|
| 59 |
+
.type('Angular');
|
| 60 |
+
cy.wait('@search');
|
| 61 |
+
cy.get('.search-results')
|
| 62 |
+
.should('not.exist');
|
| 63 |
+
});
|
| 64 |
+
|
| 65 |
+
it('should display the books list', () => {
|
| 66 |
+
cy.intercept('https://api5.angular-buch.com/books', {
|
| 67 |
+
fixture: 'books.json',
|
| 68 |
+
});
|
| 69 |
+
cy.get('nav')
|
| 70 |
+
.contains('Books')
|
| 71 |
+
.click();
|
| 72 |
+
cy.get('.book-list')
|
| 73 |
+
.children()
|
| 74 |
+
.its('length')
|
| 75 |
+
.should('eq', 1);
|
| 76 |
+
});
|
| 77 |
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"isbn": "0123456789",
|
| 4 |
+
"title": "Dummy title",
|
| 5 |
+
"authors": [
|
| 6 |
+
"John Doe"
|
| 7 |
+
],
|
| 8 |
+
"published": "2022-08-01T12:00:00.000Z",
|
| 9 |
+
"subtitle": "",
|
| 10 |
+
"description": "Some description",
|
| 11 |
+
"thumbnailUrl": "https://via.placeholder.com/500.png"
|
| 12 |
+
}
|
| 13 |
+
]
|
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "Using fixtures to represent data",
|
| 3 |
+
"email": "[email protected]"
|
| 4 |
+
}
|
| 5 |
+
|
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// ***********************************************
|
| 2 |
+
// This example namespace declaration will help
|
| 3 |
+
// with Intellisense and code completion in your
|
| 4 |
+
// IDE or Text Editor.
|
| 5 |
+
// ***********************************************
|
| 6 |
+
// declare namespace Cypress {
|
| 7 |
+
// interface Chainable<Subject = any> {
|
| 8 |
+
// customCommand(param: any): typeof customCommand;
|
| 9 |
+
// }
|
| 10 |
+
// }
|
| 11 |
+
//
|
| 12 |
+
// function customCommand(param: any): void {
|
| 13 |
+
// console.warn(param);
|
| 14 |
+
// }
|
| 15 |
+
//
|
| 16 |
+
// NOTE: You can use it like so:
|
| 17 |
+
// Cypress.Commands.add('customCommand', customCommand);
|
| 18 |
+
//
|
| 19 |
+
// ***********************************************
|
| 20 |
+
// This example commands.js shows you how to
|
| 21 |
+
// create various custom commands and overwrite
|
| 22 |
+
// existing commands.
|
| 23 |
+
//
|
| 24 |
+
// For more comprehensive examples of custom
|
| 25 |
+
// commands please read more here:
|
| 26 |
+
// https://on.cypress.io/custom-commands
|
| 27 |
+
// ***********************************************
|
| 28 |
+
//
|
| 29 |
+
//
|
| 30 |
+
// -- This is a parent command --
|
| 31 |
+
// Cypress.Commands.add("login", (email, password) => { ... })
|
| 32 |
+
//
|
| 33 |
+
//
|
| 34 |
+
// -- This is a child command --
|
| 35 |
+
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
| 36 |
+
//
|
| 37 |
+
//
|
| 38 |
+
// -- This is a dual command --
|
| 39 |
+
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
| 40 |
+
//
|
| 41 |
+
//
|
| 42 |
+
// -- This will overwrite an existing command --
|
| 43 |
+
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html>
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8">
|
| 5 |
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
| 6 |
+
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
| 7 |
+
<title>Components App</title>
|
| 8 |
+
</head>
|
| 9 |
+
<body>
|
| 10 |
+
<div data-cy-root></div>
|
| 11 |
+
</body>
|
| 12 |
+
</html>
|
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// ***********************************************************
|
| 2 |
+
// This example support/component.ts 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 |
+
|
| 22 |
+
import { mount } from 'cypress/angular'
|
| 23 |
+
|
| 24 |
+
// Augment the Cypress namespace to include type definitions for
|
| 25 |
+
// your custom command.
|
| 26 |
+
// Alternatively, can be defined in cypress/support/component.d.ts
|
| 27 |
+
// with a <reference path="./component" /> at the top of your spec.
|
| 28 |
+
declare global {
|
| 29 |
+
namespace Cypress {
|
| 30 |
+
interface Chainable {
|
| 31 |
+
mount: typeof mount
|
| 32 |
+
}
|
| 33 |
+
}
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
Cypress.Commands.add('mount', mount)
|
| 37 |
+
|
| 38 |
+
// Example use:
|
| 39 |
+
// cy.mount(MyComponent)
|
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// ***********************************************************
|
| 2 |
+
// This example support/e2e.ts 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 |
+
// When a command from ./commands is ready to use, import with `import './commands'` syntax
|
| 17 |
+
// import './commands';
|
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"extends": "../tsconfig.json",
|
| 3 |
+
"include": ["**/*.ts"],
|
| 4 |
+
"compilerOptions": {
|
| 5 |
+
"sourceMap": false,
|
| 6 |
+
"types": ["cypress"]
|
| 7 |
+
}
|
| 8 |
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { defineConfig } from 'cypress'
|
| 2 |
+
|
| 3 |
+
export default defineConfig({
|
| 4 |
+
|
| 5 |
+
e2e: {
|
| 6 |
+
'baseUrl': 'http://localhost:4200'
|
| 7 |
+
},
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
component: {
|
| 11 |
+
devServer: {
|
| 12 |
+
framework: 'angular',
|
| 13 |
+
bundler: 'webpack',
|
| 14 |
+
},
|
| 15 |
+
specPattern: '**/*.cy.ts'
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
})
|
|
@@ -7,7 +7,10 @@
|
|
| 7 |
"build": "ng build",
|
| 8 |
"watch": "ng build --watch --configuration development",
|
| 9 |
"test": "ng test",
|
| 10 |
-
"lint": "ng lint"
|
|
|
|
|
|
|
|
|
|
| 11 |
},
|
| 12 |
"private": true,
|
| 13 |
"dependencies": {
|
|
@@ -29,6 +32,7 @@
|
|
| 29 |
"@angular-devkit/build-angular": "^19.2.1",
|
| 30 |
"@angular/cli": "^19.2.1",
|
| 31 |
"@angular/compiler-cli": "^19.2.0",
|
|
|
|
| 32 |
"@types/jasmine": "~5.1.0",
|
| 33 |
"angular-eslint": "19.2.0",
|
| 34 |
"eslint": "^9.21.0",
|
|
@@ -39,6 +43,7 @@
|
|
| 39 |
"karma-jasmine": "~5.1.0",
|
| 40 |
"karma-jasmine-html-reporter": "~2.1.0",
|
| 41 |
"typescript": "~5.7.2",
|
| 42 |
-
"typescript-eslint": "8.25.0"
|
|
|
|
| 43 |
}
|
| 44 |
}
|
| 7 |
"build": "ng build",
|
| 8 |
"watch": "ng build --watch --configuration development",
|
| 9 |
"test": "ng test",
|
| 10 |
+
"lint": "ng lint",
|
| 11 |
+
"e2e": "ng e2e",
|
| 12 |
+
"cypress:open": "cypress open",
|
| 13 |
+
"cypress:run": "cypress run"
|
| 14 |
},
|
| 15 |
"private": true,
|
| 16 |
"dependencies": {
|
| 32 |
"@angular-devkit/build-angular": "^19.2.1",
|
| 33 |
"@angular/cli": "^19.2.1",
|
| 34 |
"@angular/compiler-cli": "^19.2.0",
|
| 35 |
+
"@cypress/schematic": "^3.0.0",
|
| 36 |
"@types/jasmine": "~5.1.0",
|
| 37 |
"angular-eslint": "19.2.0",
|
| 38 |
"eslint": "^9.21.0",
|
| 43 |
"karma-jasmine": "~5.1.0",
|
| 44 |
"karma-jasmine-html-reporter": "~2.1.0",
|
| 45 |
"typescript": "~5.7.2",
|
| 46 |
+
"typescript-eslint": "8.25.0",
|
| 47 |
+
"cypress": "latest"
|
| 48 |
}
|
| 49 |
}
|
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { HttpClientModule } from '@angular/common/http';
|
| 2 |
+
import { BookStoreService } from '../../shared/book-store.service';
|
| 3 |
+
import { BookFormComponent } from './book-form.component';
|
| 4 |
+
|
| 5 |
+
describe('BookFormComponent', () => {
|
| 6 |
+
it('should be able to fill required fields', () => {
|
| 7 |
+
cy.mount(BookFormComponent, {
|
| 8 |
+
providers: [BookStoreService],
|
| 9 |
+
imports: [HttpClientModule]
|
| 10 |
+
});
|
| 11 |
+
|
| 12 |
+
cy.get('#title').type('My Title');
|
| 13 |
+
cy.get('#isbn').type('0123456789');
|
| 14 |
+
cy.get('[aria-label="Author 0"').type('Erika Mustermann');
|
| 15 |
+
cy.get('#description').type('My very first book');
|
| 16 |
+
cy.get('#published').type('2022-09-10');
|
| 17 |
+
});
|
| 18 |
+
|
| 19 |
+
it('should prefill form with an existing book', () => {
|
| 20 |
+
cy.mount(`<bm-book-form [book]="book" (submitBook)="submitBook.emit($event)"></bm-book-form>`, {
|
| 21 |
+
declarations: [BookFormComponent],
|
| 22 |
+
providers: [BookStoreService],
|
| 23 |
+
imports: [HttpClientModule],
|
| 24 |
+
componentProperties: {
|
| 25 |
+
book: {
|
| 26 |
+
isbn: '0123456789',
|
| 27 |
+
title: 'My Title',
|
| 28 |
+
authors: ['Erika Mustermann'],
|
| 29 |
+
published: '2022-09-10'
|
| 30 |
+
},
|
| 31 |
+
submitBook: {
|
| 32 |
+
emit: cy.spy().as('submitBookSpy')
|
| 33 |
+
}
|
| 34 |
+
}
|
| 35 |
+
});
|
| 36 |
+
|
| 37 |
+
//cy.get('button[type=submit]').as('submitBtn').should('not.have.attr', 'disabled').click()
|
| 38 |
+
cy.get('button[type=submit]').click();
|
| 39 |
+
// Assert
|
| 40 |
+
cy.get('@submitBookSpy').should('have.been.calledWith', 1);
|
| 41 |
+
});
|
| 42 |
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { IsbnPipe } from '../shared/isbn.pipe';
|
| 2 |
+
import { BookListItemComponent } from './book-list-item.component';
|
| 3 |
+
|
| 4 |
+
describe('BookListItemComponent', () => {
|
| 5 |
+
it('should display the book with formatted ISBN', () => {
|
| 6 |
+
cy.mount(`<bm-book-list-item [book]="{
|
| 7 |
+
isbn: '0123456789',
|
| 8 |
+
title: 'Some Book',
|
| 9 |
+
authors: ['Author 1']
|
| 10 |
+
}"></bm-book-list-item>`, {
|
| 11 |
+
declarations: [BookListItemComponent, IsbnPipe],
|
| 12 |
+
});
|
| 13 |
+
|
| 14 |
+
cy.get('a').contains('ISBN 012-3456789');
|
| 15 |
+
});
|
| 16 |
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { HttpClientModule } from '@angular/common/http';
|
| 2 |
+
import { BookStoreService } from './../shared/book-store.service';
|
| 3 |
+
import { SearchComponent } from './search.component';
|
| 4 |
+
|
| 5 |
+
describe('SearchComponent', () => {
|
| 6 |
+
it('should display search results', () => {
|
| 7 |
+
cy.mount(`<bm-search></bm-search>`, {
|
| 8 |
+
declarations: [SearchComponent],
|
| 9 |
+
providers: [BookStoreService],
|
| 10 |
+
imports: [HttpClientModule]
|
| 11 |
+
});
|
| 12 |
+
|
| 13 |
+
const book = {
|
| 14 |
+
isbn: '0123456789',
|
| 15 |
+
title: 'Some Book',
|
| 16 |
+
authors: ['Author 1']
|
| 17 |
+
};
|
| 18 |
+
cy.intercept(
|
| 19 |
+
'GET',
|
| 20 |
+
'https://api5.angular-buch.com/books/search/*',
|
| 21 |
+
{ body: [book] }
|
| 22 |
+
).as('search');
|
| 23 |
+
|
| 24 |
+
cy.get('input[type=search]')
|
| 25 |
+
.clear()
|
| 26 |
+
.type(book.title)
|
| 27 |
+
.wait('@search');
|
| 28 |
+
|
| 29 |
+
cy.get('.search-results > li')
|
| 30 |
+
.its('length').should('eq', 1);
|
| 31 |
+
});
|
| 32 |
+
});
|
|
@@ -31,10 +31,10 @@ export class BookStoreService {
|
|
| 31 |
|
| 32 |
getAllSearch(term: string): Observable<Book[]> {
|
| 33 |
return this.http.get<Book[]>(`${this.apiUrl}/books/search/${term}`).pipe(
|
| 34 |
-
catchError(err => {
|
| 35 |
console.error(err);
|
| 36 |
return of([]);
|
| 37 |
-
})
|
| 38 |
);
|
| 39 |
}
|
| 40 |
|
| 31 |
|
| 32 |
getAllSearch(term: string): Observable<Book[]> {
|
| 33 |
return this.http.get<Book[]>(`${this.apiUrl}/books/search/${term}`).pipe(
|
| 34 |
+
/*catchError(err => {
|
| 35 |
console.error(err);
|
| 36 |
return of([]);
|
| 37 |
+
})*/
|
| 38 |
);
|
| 39 |
}
|
| 40 |
|