|
1 | | -import { ComponentFixture, TestBed } from '@angular/core/testing'; |
2 | | -import { NO_ERRORS_SCHEMA } from '@angular/core'; |
3 | | -import { TranslateModule } from '@ngx-translate/core'; |
4 | | -import { RouterTestingModule } from '@angular/router/testing'; |
5 | | -import { HttpClientTestingModule } from '@angular/common/http/testing'; |
6 | | -import { MarkdownModule } from 'ngx-markdown'; |
7 | | - |
8 | 1 | import { BlogEntryDetailComponent } from './blog-entry-detail.component'; |
| 2 | +import { convertToParamMap } from '@angular/router'; |
9 | 3 |
|
10 | 4 | describe('BlogEntryDetailComponent', () => { |
11 | | - let component: BlogEntryDetailComponent; |
12 | | - let fixture: ComponentFixture<BlogEntryDetailComponent>; |
13 | | - |
14 | | - beforeEach(async () => { |
15 | | - await TestBed.configureTestingModule({ |
16 | | - schemas: [NO_ERRORS_SCHEMA], |
17 | | - imports: [BlogEntryDetailComponent, HttpClientTestingModule, RouterTestingModule, TranslateModule.forRoot(), MarkdownModule.forRoot()] |
18 | | - }) |
19 | | - .compileComponents(); |
20 | | - |
21 | | - fixture = TestBed.createComponent(BlogEntryDetailComponent); |
22 | | - component = fixture.componentInstance; |
23 | | - }); |
| 5 | + const buildComponent = (slugOrId = 'entry-slug', serviceOverrides?: Partial<any>) => { |
| 6 | + const route = { |
| 7 | + snapshot: { |
| 8 | + paramMap: convertToParamMap({ slugOrId }) |
| 9 | + } |
| 10 | + } as any; |
| 11 | + const router = { navigate: jasmine.createSpy('navigate') } as any; |
| 12 | + const domeBlogService = { |
| 13 | + getBlogEntries: jasmine.createSpy('getBlogEntries').and.resolveTo([ |
| 14 | + { _id: 'entry-1', slug: 'entry-slug' } |
| 15 | + ]), |
| 16 | + getBlogEntryById: jasmine.createSpy('getBlogEntryById').and.resolveTo({ |
| 17 | + _id: 'entry-1', |
| 18 | + title: 'Entry title', |
| 19 | + metaDescription: 'Meta description', |
| 20 | + content: 'Body content' |
| 21 | + }), |
| 22 | + deleteBlogEntry: jasmine.createSpy('deleteBlogEntry').and.resolveTo({ ok: true }), |
| 23 | + ...serviceOverrides |
| 24 | + } as any; |
| 25 | + |
| 26 | + const localStorageService = { |
| 27 | + getObject: jasmine.createSpy('getObject').and.returnValue({}) |
| 28 | + } as any; |
| 29 | + const titleService = { setTitle: jasmine.createSpy('setTitle') } as any; |
| 30 | + const metaService = { updateTag: jasmine.createSpy('updateTag') } as any; |
| 31 | + |
| 32 | + const component = new BlogEntryDetailComponent( |
| 33 | + route, |
| 34 | + router, |
| 35 | + domeBlogService, |
| 36 | + localStorageService, |
| 37 | + titleService, |
| 38 | + metaService |
| 39 | + ); |
| 40 | + |
| 41 | + return { component, router, domeBlogService, titleService, metaService }; |
| 42 | + }; |
24 | 43 |
|
25 | 44 | it('should create', () => { |
| 45 | + const { component } = buildComponent(); |
26 | 46 | expect(component).toBeTruthy(); |
27 | 47 | }); |
| 48 | + |
| 49 | + it('should resolve slug to entry and apply seo metadata on init', async () => { |
| 50 | + const { component, domeBlogService, titleService, metaService } = buildComponent('entry-slug'); |
| 51 | + |
| 52 | + await component.ngOnInit(); |
| 53 | + |
| 54 | + expect(domeBlogService.getBlogEntries).toHaveBeenCalled(); |
| 55 | + expect(domeBlogService.getBlogEntryById).toHaveBeenCalledWith('entry-1'); |
| 56 | + expect(titleService.setTitle).toHaveBeenCalledWith('Entry title'); |
| 57 | + expect(metaService.updateTag).toHaveBeenCalledWith({ name: 'description', content: 'Meta description' }); |
| 58 | + }); |
| 59 | + |
| 60 | + it('should return featured image from string or object', () => { |
| 61 | + const { component } = buildComponent(); |
| 62 | + component.entry = { featuredImage: ' https://cdn/test.png ' }; |
| 63 | + expect(component.getFeaturedImage()).toBe('https://cdn/test.png'); |
| 64 | + |
| 65 | + component.entry = { featuredImage: { url: ' https://cdn/obj.png ' } }; |
| 66 | + expect(component.getFeaturedImage()).toBe('https://cdn/obj.png'); |
| 67 | + }); |
| 68 | + |
| 69 | + it('should normalize tags from array and csv', () => { |
| 70 | + const { component } = buildComponent(); |
| 71 | + component.entry = { tags: [' ai ', '', 'news'] }; |
| 72 | + expect(component.getEntryTags()).toEqual(['ai', 'news']); |
| 73 | + |
| 74 | + component.entry = { tags: 'one, two, , three' }; |
| 75 | + expect(component.getEntryTags()).toEqual(['one', 'two', 'three']); |
| 76 | + }); |
| 77 | + |
| 78 | + it('should open delete confirmation when entry id exists', () => { |
| 79 | + const { component } = buildComponent(); |
| 80 | + component.entry = { _id: 'entry-2', title: 'Delete me' }; |
| 81 | + |
| 82 | + component.openDeleteDialog(); |
| 83 | + |
| 84 | + expect(component.showDeleteConfirm).toBeTrue(); |
| 85 | + expect(component.deleteConfirmMessage).toContain('Delete me'); |
| 86 | + }); |
| 87 | + |
| 88 | + it('should delete entry and navigate back', async () => { |
| 89 | + const { component, domeBlogService, router } = buildComponent(); |
| 90 | + component.entry = { _id: 'entry-5', title: 'Post' }; |
| 91 | + |
| 92 | + await component.confirmDeleteEntry(); |
| 93 | + |
| 94 | + expect(domeBlogService.deleteBlogEntry).toHaveBeenCalledWith('entry-5'); |
| 95 | + expect(router.navigate).toHaveBeenCalledWith(['/blog']); |
| 96 | + expect(component.deleting).toBeFalse(); |
| 97 | + }); |
| 98 | + |
| 99 | + it('should fallback to id lookup for object ids', async () => { |
| 100 | + const objectId = '507f1f77bcf86cd799439011'; |
| 101 | + const { component, domeBlogService } = buildComponent(objectId); |
| 102 | + domeBlogService.getBlogEntryById.and.resolveTo({ _id: objectId, title: 'By id' }); |
| 103 | + |
| 104 | + const result = await component.getEntryBySlugOrId(objectId); |
| 105 | + |
| 106 | + expect(domeBlogService.getBlogEntryById).toHaveBeenCalledWith(objectId); |
| 107 | + expect(result._id).toBe(objectId); |
| 108 | + }); |
28 | 109 | }); |
0 commit comments