Skip to content

Commit d47f0c0

Browse files
committed
ENGAGE-243
1 parent ab5b6bb commit d47f0c0

4 files changed

Lines changed: 365 additions & 0 deletions

File tree

src/app/core/record-works/record-works.service.spec.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import { Config } from 'src/app/types/config.endpoint'
2525

2626
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'
2727

28+
declare const runtimeEnvironment: { API_WEB: string }
29+
2830
describe('RecordWorksService', () => {
2931
let service: RecordWorksService
3032
let togglzService: TogglzService
@@ -47,6 +49,7 @@ describe('RecordWorksService', () => {
4749
MatSnackBar,
4850
MatDialog,
4951
Overlay,
52+
{ provide: TogglzService, useValue: fakeTogglzService },
5053
],
5154
})
5255
service = TestBed.inject(RecordWorksService)
@@ -97,6 +100,76 @@ describe('RecordWorksService', () => {
97100
expect(requestGroupingSuggestions.length).toEqual(1)
98101
requestGroupingSuggestions.forEach((grouping) => grouping.flush({}))
99102
})
103+
104+
describe('getImportWorksDialogDataSkeleton', () => {
105+
it('returns dialog data with loading true and empty link lists', () => {
106+
const skeleton = service.getImportWorksDialogDataSkeleton()
107+
expect(skeleton.loading).toBe(true)
108+
expect(skeleton.certifiedLinks).toEqual([])
109+
expect(skeleton.moreServicesLinks).toEqual([])
110+
expect(skeleton.title).toBeDefined()
111+
expect(skeleton.introText).toBeDefined()
112+
expect(skeleton.supportLink).toBeDefined()
113+
expect(skeleton.certifiedSectionHeading).toBeDefined()
114+
expect(skeleton.moreServicesHeading).toBeDefined()
115+
expect(skeleton.connectNowLabel).toBeDefined()
116+
expect(skeleton.connectedLabel).toBeDefined()
117+
})
118+
})
119+
120+
describe('loadSearchAndLinkWizardDialogData', () => {
121+
it('requests retrieve-works-search-and-link-wizard.json and returns mapped dialog data', (done) => {
122+
const listPayload = [
123+
{
124+
id: 'cert-1',
125+
name: 'Certified Service',
126+
redirectUri: 'https://app.example/cb',
127+
scopes: '/read-limited',
128+
redirectUriMetadata: { type: 'Certified', defaultDescription: 'Cert desc' },
129+
},
130+
{
131+
id: 'more-1',
132+
name: 'More Service',
133+
description: 'More desc',
134+
redirectUri: 'https://app.example/cb2',
135+
scopes: '/activities/update',
136+
},
137+
]
138+
139+
service.loadSearchAndLinkWizardDialogData('en').subscribe((data) => {
140+
expect(data.loading).toBe(false)
141+
expect(data.certifiedLinks.length).toBe(1)
142+
expect(data.certifiedLinks[0].name).toBe('Certified Service')
143+
expect(data.certifiedLinks[0].description).toBe('Cert desc')
144+
expect(data.moreServicesLinks.length).toBe(1)
145+
expect(data.moreServicesLinks[0].name).toBe('More Service')
146+
expect(data.moreServicesLinks[0].description).toBe('More desc')
147+
done()
148+
})
149+
150+
const req = httpTestingController.expectOne(
151+
runtimeEnvironment.API_WEB +
152+
'workspace/retrieve-works-search-and-link-wizard.json'
153+
)
154+
expect(req.request.method).toBe('GET')
155+
req.flush(listPayload)
156+
})
157+
158+
it('returns empty certified and more lists when API returns empty array', (done) => {
159+
service.loadSearchAndLinkWizardDialogData('en').subscribe((data) => {
160+
expect(data.certifiedLinks).toEqual([])
161+
expect(data.moreServicesLinks).toEqual([])
162+
expect(data.loading).toBe(false)
163+
done()
164+
})
165+
166+
const req = httpTestingController.expectOne(
167+
runtimeEnvironment.API_WEB +
168+
'workspace/retrieve-works-search-and-link-wizard.json'
169+
)
170+
req.flush([])
171+
})
172+
})
100173
})
101174

102175
function getNumberOfWorks(numberOfContributors: number): Work[] {
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import { HttpClient, HTTP_INTERCEPTORS } from '@angular/common/http'
2+
import {
3+
HttpClientTestingModule,
4+
HttpTestingController,
5+
} from '@angular/common/http/testing'
6+
import { TestBed } from '@angular/core/testing'
7+
import { CookieService } from 'ngx-cookie-service'
8+
import { XsrfFallbackInterceptor } from './xsrf-fallback.interceptor'
9+
10+
describe('XsrfFallbackInterceptor', () => {
11+
let http: HttpClient
12+
let httpMock: HttpTestingController
13+
let cookieGetSpy: jasmine.Spy
14+
15+
const apiBase = 'http://api.example/'
16+
const baseUrl = 'http://orcid.example/'
17+
const authBase = 'http://auth.example/'
18+
19+
beforeEach(() => {
20+
;(window as any).runtimeEnvironment = {
21+
production: false,
22+
API_WEB: apiBase,
23+
BASE_URL: baseUrl,
24+
AUTH_SERVER: authBase,
25+
}
26+
cookieGetSpy = jasmine.createSpy('get').and.returnValue('')
27+
28+
TestBed.configureTestingModule({
29+
imports: [HttpClientTestingModule],
30+
providers: [
31+
{
32+
provide: HTTP_INTERCEPTORS,
33+
useClass: XsrfFallbackInterceptor,
34+
multi: true,
35+
},
36+
{ provide: CookieService, useValue: { get: cookieGetSpy } },
37+
],
38+
})
39+
40+
http = TestBed.inject(HttpClient)
41+
httpMock = TestBed.inject(HttpTestingController)
42+
})
43+
44+
afterEach(() => {
45+
httpMock.verify()
46+
})
47+
48+
it('passes through without adding header when production is true', () => {
49+
const env = (window as any).runtimeEnvironment
50+
env.production = true
51+
cookieGetSpy.and.returnValue('xsrf-token-123')
52+
53+
http.post(apiBase + 'works/work.json', {}).subscribe()
54+
55+
const req = httpMock.expectOne(apiBase + 'works/work.json')
56+
expect(req.request.headers.has('x-xsrf-token')).toBe(false)
57+
req.flush({})
58+
env.production = false
59+
})
60+
61+
it('passes through GET requests without adding header', () => {
62+
http.get(apiBase + 'works/works.json').subscribe()
63+
64+
const req = httpMock.expectOne(apiBase + 'works/works.json')
65+
expect(req.request.headers.has('x-xsrf-token')).toBe(false)
66+
req.flush({})
67+
})
68+
69+
it('passes through when x-xsrf-token header is already present', () => {
70+
cookieGetSpy.and.returnValue('cookie-token')
71+
http
72+
.post(apiBase + 'works/work.json', {}, {
73+
headers: { 'x-xsrf-token': 'existing-token' },
74+
})
75+
.subscribe()
76+
77+
const req = httpMock.expectOne(apiBase + 'works/work.json')
78+
expect(req.request.headers.get('x-xsrf-token')).toBe('existing-token')
79+
req.flush({})
80+
})
81+
82+
it('passes through when request URL is not to backend host', () => {
83+
cookieGetSpy.and.returnValue('token')
84+
http.post('https://other-origin.com/api', {}).subscribe()
85+
86+
const req = httpMock.expectOne('https://other-origin.com/api')
87+
expect(req.request.headers.has('x-xsrf-token')).toBe(false)
88+
req.flush({})
89+
})
90+
91+
it('passes through when cookie is missing', () => {
92+
cookieGetSpy.and.returnValue('')
93+
94+
http.post(apiBase + 'works/work.json', {}).subscribe()
95+
96+
const req = httpMock.expectOne(apiBase + 'works/work.json')
97+
expect(req.request.headers.has('x-xsrf-token')).toBe(false)
98+
req.flush({})
99+
})
100+
101+
it('adds XSRF-TOKEN for POST to API_WEB when cookie is present', () => {
102+
cookieGetSpy.and.callFake((name: string) =>
103+
name === 'XSRF-TOKEN' ? 'api-xsrf-token' : ''
104+
)
105+
106+
http.post(apiBase + 'works/work.json', {}).subscribe()
107+
108+
const req = httpMock.expectOne(apiBase + 'works/work.json')
109+
expect(req.request.headers.get('x-xsrf-token')).toBe('api-xsrf-token')
110+
expect(req.request.withCredentials).toBe(true)
111+
req.flush({})
112+
})
113+
114+
it('adds AUTH-XSRF-TOKEN for POST to AUTH_SERVER when cookie is present', () => {
115+
cookieGetSpy.and.callFake((name: string) =>
116+
name === 'AUTH-XSRF-TOKEN' ? 'auth-xsrf-token' : ''
117+
)
118+
119+
http.post(authBase + 'signin/auth.json', {}).subscribe()
120+
121+
const req = httpMock.expectOne(authBase + 'signin/auth.json')
122+
expect(req.request.headers.get('x-xsrf-token')).toBe('auth-xsrf-token')
123+
req.flush({})
124+
})
125+
126+
it('adds XSRF-TOKEN for relative URL when cookie is present', () => {
127+
cookieGetSpy.and.returnValue('rel-xsrf-token')
128+
129+
http.post('/signin/auth.json', {}).subscribe()
130+
131+
const req = httpMock.expectOne('/signin/auth.json')
132+
expect(req.request.headers.get('x-xsrf-token')).toBe('rel-xsrf-token')
133+
req.flush({})
134+
})
135+
136+
it('passes through PUT and PATCH and DELETE when not production', () => {
137+
cookieGetSpy.and.returnValue('token')
138+
139+
http.put(apiBase + 'works/1.json', {}).subscribe()
140+
let req = httpMock.expectOne(apiBase + 'works/1.json')
141+
expect(req.request.headers.get('x-xsrf-token')).toBe('token')
142+
req.flush({})
143+
144+
http.patch(apiBase + 'works/1.json', {}).subscribe()
145+
req = httpMock.expectOne(apiBase + 'works/1.json')
146+
expect(req.request.headers.get('x-xsrf-token')).toBe('token')
147+
req.flush({})
148+
149+
http.delete(apiBase + 'works/1.json').subscribe()
150+
req = httpMock.expectOne(apiBase + 'works/1.json')
151+
expect(req.request.headers.get('x-xsrf-token')).toBe('token')
152+
req.flush({})
153+
})
154+
})

src/app/record/components/search-link-wizard/search-link-wizard.component.spec.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
import { ComponentFixture, TestBed } from '@angular/core/testing'
22

33
import { SearchLinkWizardComponent } from './search-link-wizard.component'
4+
import { RecordImportWizard } from '../../../types/record-peer-review-import.endpoint'
45

56
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'
67

8+
declare const runtimeEnvironment: { BASE_URL: string }
9+
710
describe('SearchLinkWizardComponent', () => {
811
let component: SearchLinkWizardComponent
912
let fixture: ComponentFixture<SearchLinkWizardComponent>
1013

1114
beforeEach(async () => {
15+
;(window as any).runtimeEnvironment = { BASE_URL: 'https://example.org/' }
1216
await TestBed.configureTestingModule({
1317
declarations: [SearchLinkWizardComponent],
1418
schemas: [CUSTOM_ELEMENTS_SCHEMA],
@@ -18,10 +22,85 @@ describe('SearchLinkWizardComponent', () => {
1822
beforeEach(() => {
1923
fixture = TestBed.createComponent(SearchLinkWizardComponent)
2024
component = fixture.componentInstance
25+
component.recordImportWizards = []
2126
fixture.detectChanges()
2227
})
2328

2429
it('should create', () => {
2530
expect(component).toBeTruthy()
2631
})
32+
33+
describe('openImportWizardUrlFilter', () => {
34+
it('returns clientWebsite when client is connected', () => {
35+
const client: RecordImportWizard = {
36+
id: 'client-1',
37+
name: 'Test',
38+
redirectUri: 'https://app.example/cb',
39+
scopes: 'scope1',
40+
isConnected: true,
41+
clientWebsite: 'https://connected.example',
42+
}
43+
expect(component.openImportWizardUrlFilter(client)).toBe(
44+
'https://connected.example'
45+
)
46+
})
47+
48+
it('returns clientWebsite when status is RETIRED', () => {
49+
const client: RecordImportWizard = {
50+
id: 'client-2',
51+
name: 'Retired',
52+
redirectUri: 'https://app.example/cb',
53+
scopes: 'scope1',
54+
status: 'RETIRED',
55+
clientWebsite: 'https://retired.example',
56+
}
57+
expect(component.openImportWizardUrlFilter(client)).toBe(
58+
'https://retired.example'
59+
)
60+
})
61+
62+
63+
it('builds OAuth authorize URL when not connected and not retired', () => {
64+
const client: RecordImportWizard = {
65+
id: 'my-client',
66+
name: 'OAuth App',
67+
redirectUri: 'https://app.example/callback',
68+
scopes: '/read-limited',
69+
}
70+
const url = component.openImportWizardUrlFilter(client)
71+
expect(url).toContain('https://example.org/oauth/authorize')
72+
expect(url).toContain('client_id=my-client')
73+
expect(url).toContain('response_type=code')
74+
expect(url).toContain('scope=' + encodeURIComponent('/read-limited'))
75+
expect(url).toContain(
76+
'redirect_uri=' + encodeURIComponent('https://app.example/callback')
77+
)
78+
})
79+
})
80+
81+
describe('toggle', () => {
82+
it('flips show from false to true', () => {
83+
const wizard: RecordImportWizard = {
84+
id: 'w',
85+
name: 'W',
86+
redirectUri: 'https://u',
87+
scopes: 's',
88+
show: false,
89+
}
90+
component.toggle(wizard)
91+
expect(wizard.show).toBe(true)
92+
})
93+
94+
it('flips show from true to false', () => {
95+
const wizard: RecordImportWizard = {
96+
id: 'w',
97+
name: 'W',
98+
redirectUri: 'https://u',
99+
scopes: 's',
100+
show: true,
101+
}
102+
component.toggle(wizard)
103+
expect(wizard.show).toBe(false)
104+
})
105+
})
27106
})

0 commit comments

Comments
 (0)