Skip to content

Commit

Permalink
Merge pull request #340 from PermanentOrg/featured-archives-api
Browse files Browse the repository at this point in the history
Fetch featured archives from API
  • Loading branch information
meisekimiu authored Feb 1, 2024
2 parents 39156fe + 7223861 commit 5c1e771
Show file tree
Hide file tree
Showing 19 changed files with 398 additions and 160 deletions.
1 change: 0 additions & 1 deletion .github/workflows/build-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ jobs:
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY_DEV }}
FIREBASE_API_KEY: ${{ secrets.FIREBASE_API_KEY_DEV }}
STRIPE_API_KEY: ${{ secrets.STRIPE_TEST_KEY }}
FEATURED_ARCHIVES: ${{ secrets.FEATURED_ARCHIVES_DEV }}
RECAPTCHA_API_KEY: ${{ secrets.RECAPTCHA_API_KEY_DEV }}
STELA_DOMAIN: ${{ secrets.STELA_DOMAIN_DEV }}
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN_DEV }}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
<!-- @format -->
<div [class]="getClasses()">
<div class="banner" prBgImage [bgSrc]="bannerURL">
<div class="profile-pic"><img [src]="thumbURL" /></div>
<div [class]="classNames">
<div class="banner" prBgImage [bgSrc]="archive.bannerImage">
<div class="profile-pic">
<img [src]="archive.profileImage" [alt]="archive.name" />
</div>
</div>
<div class="contents">
<span class="archive-type">{{ getArchiveType() }} Archive</span>
<span class="archive-name">{{ archive.name }}</span>
<span class="archive-description">{{ archive.description }}</span>
<a [href]="getArchiveLink()" class="archive-link"
<span class="archive-type">{{ archive.type | archiveTypeName }}</span>
<span class="archive-name">The {{ archive.name }} Archive</span>
<span class="archive-description"></span>
<a
[href]="getArchiveLink()"
class="archive-link"
[ariaLabel]="'Explore The ' + archive.name + ' Archive'"
>Explore the archive &emsp;
<fa-icon [icon]="['fas', 'arrow-right']"></fa-icon
></a>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/* @format */
import { Shallow } from 'shallow-render';
import { ArchiveType } from '@models/archive-vo';
import { FeaturedArchive } from '../../types/featured-archive';
import { GalleryModule } from '../../gallery.module';
import { FeaturedArchiveComponent } from './featured-archive.component';

const testArchive: FeaturedArchive = {
archiveNbr: '0000-0000',
name: 'Unit Testing',
type: 'type.archive.person',
profileImage: 'thumbUrl',
bannerImage: 'bannerUrl',
};

describe('FeaturedArchiveComponent', () => {
let shallow: Shallow<FeaturedArchiveComponent>;

const defaultRender = async () =>
await shallow.render(
'<pr-featured-archive [archive]="archive"></pr-featured-archive>',
{
bind: {
archive: testArchive,
},
}
);

beforeEach(() => {
shallow = new Shallow(FeaturedArchiveComponent, GalleryModule);
});

it('should exist', async () => {
const { instance } = await defaultRender();

expect(instance).toBeTruthy();
});

it('should include all archive information', async () => {
const { find, element } = await defaultRender();

expect(find('.profile-pic img').attributes.src).toBe('thumbUrl');
expect(element.nativeElement.innerText).toContain(
'The Unit Testing Archive'
);
});

it('should be able to get proper classnames', async () => {
const { instance } = await defaultRender();
function expectClassnameForArchiveType(
archiveType: ArchiveType,
expectedClassname: string
) {
instance.archive.type = archiveType;
instance.ngOnInit();

expect(instance.classNames).toContain(expectedClassname);
}

expectClassnameForArchiveType('type.archive.person', 'personal');
expectClassnameForArchiveType('type.archive.family', 'group');
expectClassnameForArchiveType('type.archive.organization', 'organization');
expectClassnameForArchiveType('type.archive.nonprofit', 'organization');
});

describe('Accessibility', () => {
it('has alt text for all img tags', async () => {
const { find } = await defaultRender();
const images = find('img');
images.forEach(() => {
expect(images.attributes.alt).not.toBeUndefined();
});
});

it('has specific label text for each link', async () => {
const { find } = await defaultRender();
const link = find('a');

expect(link.attributes['aria-label']).toContain('Unit Testing');
});
});
});
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
/* @format */
import { Component, OnInit, Input } from '@angular/core';
import { ArchiveVO } from '@models';
import { ApiService } from '@shared/services/api/api.service';
import { FeaturedArchive } from '../../types/featured-archive';

@Component({
Expand All @@ -11,26 +9,12 @@ import { FeaturedArchive } from '../../types/featured-archive';
})
export class FeaturedArchiveComponent implements OnInit {
@Input() archive: FeaturedArchive;
public thumbURL = '';
public bannerURL = '';
public classNames: string[] = ['featured-archive'];

constructor(protected api: ApiService) {}
constructor() {}

async ngOnInit() {
const archiveVO = (
await this.api.archive.get([
new ArchiveVO({ archiveNbr: this.archive.archiveNbr }),
])
).getArchiveVO();
if (archiveVO.thumbURL200) {
this.thumbURL = archiveVO.thumbURL200;
const rootFolder = (
await this.api.folder.getPublicRoot(archiveVO.archiveNbr)
).getFolderVO();
if (rootFolder.thumbArchiveNbr && rootFolder.thumbURL500) {
this.bannerURL = rootFolder.thumbURL500;
}
}
this.classNames = this.getClasses();
}

public getClasses(): string[] {
Expand All @@ -45,18 +29,6 @@ export class FeaturedArchiveComponent implements OnInit {
return classes;
}

public getArchiveType(): string {
switch (this.archive.type) {
case 'type.archive.person':
return 'Personal';
case 'type.archive.family':
return 'Group';
case 'type.archive.organization':
case 'type.archive.nonprofit':
return 'Organizational';
}
}

public getArchiveLink(): string {
return ['/p', 'archive', this.archive.archiveNbr].join('/');
}
Expand Down
10 changes: 2 additions & 8 deletions src/app/gallery/components/gallery/gallery.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,15 @@ <h2>Featured Archives</h2>
[archive]="archive"
></pr-featured-archive>
</div>
<div *ngIf="archives.length === 0" class="null-message">
<div *ngIf="!loading && archives.length === 0" class="null-message">
<p>There are no featured archives at this time.</p>
<p *ngIf="environment.startsWith('local')" class="dev-message">
To set up featured archives on your local environment, add them to your
<kbd>.env</kbd> file as a JSON string. See the
<kbd>src/app/gallery/data/featured.ts</kbd> file to see how to format
the data.
</p>
</div>
</div>
</div>
<ng-template #searchBox>
<div class="searchBox">
<fa-icon [icon]="['fas', 'search']"></fa-icon>
<pr-search-box [onPublicGallery]="true"></pr-search-box>
<pr-search-box [isPublicGallery]="true"></pr-search-box>
</div>
</ng-template>
<ng-template #archivesImg>
Expand Down
119 changes: 119 additions & 0 deletions src/app/gallery/components/gallery/gallery.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/* @format */
import { Shallow } from 'shallow-render';
import { AccountService } from '@shared/services/account/account.service';
import { FeaturedArchive } from '../../types/featured-archive';
import {
FEATURED_ARCHIVE_API,
FeaturedArchiveApi,
} from '../../types/featured-archive-api';
import { GalleryModule } from '../../gallery.module';
import { GalleryComponent } from './gallery.component';

class DummyFeaturedArchiveAPI implements FeaturedArchiveApi {
public static failRequest = false;
public static FeaturedArchives: FeaturedArchive[] = [];
public static reset(): void {
DummyFeaturedArchiveAPI.FeaturedArchives = [];
DummyFeaturedArchiveAPI.failRequest = false;
}

public fetchedFromApi: boolean = false;

public async getFeaturedArchiveList(): Promise<FeaturedArchive[]> {
if (DummyFeaturedArchiveAPI.failRequest) {
throw new Error('Forced unit test error');
}
this.fetchedFromApi = true;
return DummyFeaturedArchiveAPI.FeaturedArchives;
}
}

class DummyAccountService {
public static loggedIn: boolean = false;

public isLoggedIn(): boolean {
return DummyAccountService.loggedIn;
}
}

const testArchive: FeaturedArchive = {
archiveNbr: '0000-0000',
name: 'Unit Testing',
type: 'type.archive.person',
profileImage: 'thumbUrl',
bannerImage: 'bannerUrl',
} as const;

describe('GalleryComponent', () => {
let shallow: Shallow<GalleryComponent>;
let dummyApi: DummyFeaturedArchiveAPI;
let dummyAccount: DummyAccountService;

beforeEach(async () => {
DummyFeaturedArchiveAPI.reset();
DummyAccountService.loggedIn = false;
dummyApi = new DummyFeaturedArchiveAPI();
dummyAccount = new DummyAccountService();
shallow = new Shallow(GalleryComponent, GalleryModule);
shallow
.provide({
provide: FEATURED_ARCHIVE_API,
useValue: dummyApi,
})
.provide({
provide: AccountService,
useValue: dummyAccount,
});
shallow.dontMock(FEATURED_ARCHIVE_API, AccountService);
});

it('should fetch featured archives from the API', async () => {
await shallow.render();

expect(dummyApi.fetchedFromApi).toBeTrue();
});

it('displays the list of featured archives', async () => {
DummyFeaturedArchiveAPI.FeaturedArchives = [testArchive];
const { fixture, find } = await shallow.render();
await fixture.whenStable();

expect(find('pr-featured-archive').length).toBe(1);
});

it('does not display the error message while loading the archives', async () => {
DummyFeaturedArchiveAPI.FeaturedArchives = [testArchive];
const { find, instance } = await shallow.render();
instance.loading = true;

expect(find('.null-message').length).toBe(0);
});

it('displays an error message if no featured archives exist', async () => {
const { find } = await shallow.render();

expect(find('pr-featured-archive').length).toBe(0);
expect(find('.null-message').length).toBe(1);
});

it('displays an error message if the fetch failed', async () => {
DummyFeaturedArchiveAPI.FeaturedArchives = [testArchive];
DummyFeaturedArchiveAPI.failRequest = true;
const { find } = await shallow.render();

expect(find('.null-message').length).toBe(1);
});

it("does not display the user's public archives list if logged out", async () => {
const { find } = await shallow.render();

expect(find('pr-public-archives-list').length).toBe(0);
});

it("displays the user's public archives list if logged in", async () => {
DummyAccountService.loggedIn = true;
const { find } = await shallow.render();

expect(find('pr-public-archives-list').length).toBe(1);
});
});
43 changes: 20 additions & 23 deletions src/app/gallery/components/gallery/gallery.component.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,36 @@
/* @format */
import { Component, Inject, OnInit } from '@angular/core';
import { AccountService } from '@shared/services/account/account.service';
import { Component, OnInit } from '@angular/core';
import { environment } from '@root/environments/environment';
import { SecretsService } from '@shared/services/secrets/secrets.service';
import { FeaturedArchive } from '../../types/featured-archive';
import { featuredArchives } from '../../data/featured';
import {
FEATURED_ARCHIVE_API,
FeaturedArchiveApi,
} from '../../types/featured-archive-api';

@Component({
selector: 'pr-gallery',
templateUrl: './gallery.component.html',
styleUrls: ['./gallery.component.scss'],
})
export class GalleryComponent implements OnInit {
public environment: string = '';
public archives: FeaturedArchive[] = this.getFeaturedArchives();
public isLoggedIn: boolean;
constructor(private accountService:AccountService) {
public archives: FeaturedArchive[] = [];
public loading = true;

constructor(
@Inject(FEATURED_ARCHIVE_API) private api: FeaturedArchiveApi,
private accountService: AccountService
) {
this.isLoggedIn = this.accountService.isLoggedIn();
}

ngOnInit(): void {}

protected getFeaturedArchives(): FeaturedArchive[] {
this.environment = environment.environment;
if (
this.environment !== 'prod' &&
SecretsService.hasStatic('FEATURED_ARCHIVES')
) {
const envFeatured = SecretsService.getStatic('FEATURED_ARCHIVES');
if (envFeatured) {
const archives = JSON.parse(envFeatured) as FeaturedArchive[];
if (archives) {
return archives;
}
}
async ngOnInit() {
try {
this.archives = await this.api.getFeaturedArchiveList();
} catch {
// do nothing
} finally {
this.loading = false;
}
return featuredArchives;
}
}
Loading

0 comments on commit 5c1e771

Please sign in to comment.