@@ -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/schematics",
|
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": {
|
@@ -30,6 +33,7 @@
|
|
30 |
"@angular-devkit/build-angular": "^18.2.1",
|
31 |
"@angular/cli": "^18.2.1",
|
32 |
"@angular/compiler-cli": "^18.2.0",
|
|
|
33 |
"@types/jasmine": "~5.1.0",
|
34 |
"angular-eslint": "18.3.0",
|
35 |
"eslint": "^9.9.0",
|
@@ -40,6 +44,7 @@
|
|
40 |
"karma-jasmine": "~5.1.0",
|
41 |
"karma-jasmine-html-reporter": "~2.1.0",
|
42 |
"typescript": "~5.5.2",
|
43 |
-
"typescript-eslint": "8.1.0"
|
|
|
44 |
}
|
45 |
}
|
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": {
|
33 |
"@angular-devkit/build-angular": "^18.2.1",
|
34 |
"@angular/cli": "^18.2.1",
|
35 |
"@angular/compiler-cli": "^18.2.0",
|
36 |
+
"@cypress/schematic": "^2.5.2",
|
37 |
"@types/jasmine": "~5.1.0",
|
38 |
"angular-eslint": "18.3.0",
|
39 |
"eslint": "^9.9.0",
|
44 |
"karma-jasmine": "~5.1.0",
|
45 |
"karma-jasmine-html-reporter": "~2.1.0",
|
46 |
"typescript": "~5.5.2",
|
47 |
+
"typescript-eslint": "8.1.0",
|
48 |
+
"cypress": "latest"
|
49 |
}
|
50 |
}
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
2 |
+
|
3 |
+
import { BookCreateComponent } from './book-create.component';
|
4 |
+
|
5 |
+
describe('BookCreateComponent', () => {
|
6 |
+
let component: BookCreateComponent;
|
7 |
+
let fixture: ComponentFixture<BookCreateComponent>;
|
8 |
+
|
9 |
+
beforeEach(async () => {
|
10 |
+
await TestBed.configureTestingModule({
|
11 |
+
declarations: [ BookCreateComponent ]
|
12 |
+
})
|
13 |
+
.compileComponents();
|
14 |
+
|
15 |
+
fixture = TestBed.createComponent(BookCreateComponent);
|
16 |
+
component = fixture.componentInstance;
|
17 |
+
fixture.detectChanges();
|
18 |
+
});
|
19 |
+
|
20 |
+
it('should create', () => {
|
21 |
+
expect(component).toBeTruthy();
|
22 |
+
});
|
23 |
+
});
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
2 |
+
|
3 |
+
import { BookEditComponent } from './book-edit.component';
|
4 |
+
|
5 |
+
describe('BookEditComponent', () => {
|
6 |
+
let component: BookEditComponent;
|
7 |
+
let fixture: ComponentFixture<BookEditComponent>;
|
8 |
+
|
9 |
+
beforeEach(async () => {
|
10 |
+
await TestBed.configureTestingModule({
|
11 |
+
declarations: [ BookEditComponent ]
|
12 |
+
})
|
13 |
+
.compileComponents();
|
14 |
+
|
15 |
+
fixture = TestBed.createComponent(BookEditComponent);
|
16 |
+
component = fixture.componentInstance;
|
17 |
+
fixture.detectChanges();
|
18 |
+
});
|
19 |
+
|
20 |
+
it('should create', () => {
|
21 |
+
expect(component).toBeTruthy();
|
22 |
+
});
|
23 |
+
});
|
@@ -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,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
2 |
+
|
3 |
+
import { BookFormComponent } from './book-form.component';
|
4 |
+
|
5 |
+
describe('BookFormComponent', () => {
|
6 |
+
let component: BookFormComponent;
|
7 |
+
let fixture: ComponentFixture<BookFormComponent>;
|
8 |
+
|
9 |
+
beforeEach(async () => {
|
10 |
+
await TestBed.configureTestingModule({
|
11 |
+
declarations: [ BookFormComponent ]
|
12 |
+
})
|
13 |
+
.compileComponents();
|
14 |
+
|
15 |
+
fixture = TestBed.createComponent(BookFormComponent);
|
16 |
+
component = fixture.componentInstance;
|
17 |
+
fixture.detectChanges();
|
18 |
+
});
|
19 |
+
|
20 |
+
it('should create', () => {
|
21 |
+
expect(component).toBeTruthy();
|
22 |
+
});
|
23 |
+
});
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
2 |
+
|
3 |
+
import { FormErrorsComponent } from './form-errors.component';
|
4 |
+
|
5 |
+
describe('FormErrorsComponent', () => {
|
6 |
+
let component: FormErrorsComponent;
|
7 |
+
let fixture: ComponentFixture<FormErrorsComponent>;
|
8 |
+
|
9 |
+
beforeEach(async () => {
|
10 |
+
await TestBed.configureTestingModule({
|
11 |
+
declarations: [ FormErrorsComponent ]
|
12 |
+
})
|
13 |
+
.compileComponents();
|
14 |
+
|
15 |
+
fixture = TestBed.createComponent(FormErrorsComponent);
|
16 |
+
component = fixture.componentInstance;
|
17 |
+
fixture.detectChanges();
|
18 |
+
});
|
19 |
+
|
20 |
+
it('should create', () => {
|
21 |
+
expect(component).toBeTruthy();
|
22 |
+
});
|
23 |
+
});
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { TestBed } from '@angular/core/testing';
|
2 |
+
|
3 |
+
import { AsyncValidatorsService } from './async-validators.service';
|
4 |
+
|
5 |
+
describe('AsyncValidatorsService', () => {
|
6 |
+
let service: AsyncValidatorsService;
|
7 |
+
|
8 |
+
beforeEach(() => {
|
9 |
+
TestBed.configureTestingModule({});
|
10 |
+
service = TestBed.inject(AsyncValidatorsService);
|
11 |
+
});
|
12 |
+
|
13 |
+
it('should be created', () => {
|
14 |
+
expect(service).toBeTruthy();
|
15 |
+
});
|
16 |
+
});
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
2 |
+
|
3 |
+
import { BookDetailsComponent } from './book-details.component';
|
4 |
+
|
5 |
+
describe('BookDetailsComponent', () => {
|
6 |
+
let component: BookDetailsComponent;
|
7 |
+
let fixture: ComponentFixture<BookDetailsComponent>;
|
8 |
+
|
9 |
+
beforeEach(async () => {
|
10 |
+
await TestBed.configureTestingModule({
|
11 |
+
declarations: [ BookDetailsComponent ]
|
12 |
+
})
|
13 |
+
.compileComponents();
|
14 |
+
|
15 |
+
fixture = TestBed.createComponent(BookDetailsComponent);
|
16 |
+
component = fixture.componentInstance;
|
17 |
+
fixture.detectChanges();
|
18 |
+
});
|
19 |
+
|
20 |
+
it('should create', () => {
|
21 |
+
expect(component).toBeTruthy();
|
22 |
+
});
|
23 |
+
});
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
2 |
+
|
3 |
+
import { BookListComponent } from './book-list.component';
|
4 |
+
|
5 |
+
describe('BookListComponent', () => {
|
6 |
+
let component: BookListComponent;
|
7 |
+
let fixture: ComponentFixture<BookListComponent>;
|
8 |
+
|
9 |
+
beforeEach(async () => {
|
10 |
+
await TestBed.configureTestingModule({
|
11 |
+
declarations: [ BookListComponent ]
|
12 |
+
})
|
13 |
+
.compileComponents();
|
14 |
+
|
15 |
+
fixture = TestBed.createComponent(BookListComponent);
|
16 |
+
component = fixture.componentInstance;
|
17 |
+
fixture.detectChanges();
|
18 |
+
});
|
19 |
+
|
20 |
+
it('should create', () => {
|
21 |
+
expect(component).toBeTruthy();
|
22 |
+
});
|
23 |
+
});
|
@@ -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,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { LoggedinOnlyDirective } from './loggedin-only.directive';
|
2 |
+
|
3 |
+
describe('LoggedinOnlyDirective', () => {
|
4 |
+
it('should create an instance', () => {
|
5 |
+
const directive = new LoggedinOnlyDirective();
|
6 |
+
expect(directive).toBeTruthy();
|
7 |
+
});
|
8 |
+
});
|
@@ -1,4 +1,3 @@
|
|
1 |
-
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
2 |
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
3 |
|
4 |
import { HomeComponent } from './home.component';
|
@@ -9,8 +8,7 @@ describe('HomeComponent', () => {
|
|
9 |
|
10 |
beforeEach(async () => {
|
11 |
await TestBed.configureTestingModule({
|
12 |
-
declarations: [ HomeComponent ]
|
13 |
-
schemas: [NO_ERRORS_SCHEMA]
|
14 |
})
|
15 |
.compileComponents();
|
16 |
|
|
|
1 |
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
2 |
|
3 |
import { HomeComponent } from './home.component';
|
8 |
|
9 |
beforeEach(async () => {
|
10 |
await TestBed.configureTestingModule({
|
11 |
+
declarations: [ HomeComponent ]
|
|
|
12 |
})
|
13 |
.compileComponents();
|
14 |
|
@@ -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 |
+
});
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
2 |
+
|
3 |
+
import { SearchComponent } from './search.component';
|
4 |
+
|
5 |
+
describe('SearchComponent', () => {
|
6 |
+
let component: SearchComponent;
|
7 |
+
let fixture: ComponentFixture<SearchComponent>;
|
8 |
+
|
9 |
+
beforeEach(async () => {
|
10 |
+
await TestBed.configureTestingModule({
|
11 |
+
declarations: [ SearchComponent ]
|
12 |
+
})
|
13 |
+
.compileComponents();
|
14 |
+
|
15 |
+
fixture = TestBed.createComponent(SearchComponent);
|
16 |
+
component = fixture.componentInstance;
|
17 |
+
fixture.detectChanges();
|
18 |
+
});
|
19 |
+
|
20 |
+
it('should create', () => {
|
21 |
+
expect(component).toBeTruthy();
|
22 |
+
});
|
23 |
+
});
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { TestBed } from '@angular/core/testing';
|
2 |
+
|
3 |
+
import { BookStoreService } from './book-store.service';
|
4 |
+
|
5 |
+
describe('BookStoreService', () => {
|
6 |
+
let service: BookStoreService;
|
7 |
+
|
8 |
+
beforeEach(() => {
|
9 |
+
TestBed.configureTestingModule({});
|
10 |
+
service = TestBed.inject(BookStoreService);
|
11 |
+
});
|
12 |
+
|
13 |
+
it('should be created', () => {
|
14 |
+
expect(service).toBeTruthy();
|
15 |
+
});
|
16 |
+
});
|
@@ -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 |
|