94 lines
2.9 KiB
TypeScript
94 lines
2.9 KiB
TypeScript
|
|
import { TestBed } from '@angular/core/testing';
|
||
|
|
import { Observable, of, throwError } from 'rxjs';
|
||
|
|
import { AccountIdentitiesComponent } from './account-identities.component';
|
||
|
|
import { SOCIAL_IDENTITY_GATEWAY } from '../../../../core/identity/services/social-identity-gateway.token';
|
||
|
|
import { SocialIdentityGateway } from '../../../../core/identity/services/social-identity-gateway.interface';
|
||
|
|
import {
|
||
|
|
ExternalIdentity,
|
||
|
|
ExternalIdentityProvider,
|
||
|
|
} from '../../../../core/identity/models/customer-identity.model';
|
||
|
|
|
||
|
|
function identity(provider: ExternalIdentityProvider): ExternalIdentity {
|
||
|
|
return {
|
||
|
|
customerId: 'c1',
|
||
|
|
provider,
|
||
|
|
providerUserId: 'u_' + provider,
|
||
|
|
verifiedAt: '2026-08-01T00:00:00.000Z',
|
||
|
|
lastUsedAt: '2026-08-21T00:00:00.000Z',
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
class FakeGateway implements SocialIdentityGateway {
|
||
|
|
linked: ExternalIdentity[] = [];
|
||
|
|
unlinkCalls: ExternalIdentityProvider[] = [];
|
||
|
|
failList = false;
|
||
|
|
|
||
|
|
getAuthorizeUrl(): Observable<string> {
|
||
|
|
return of('about:blank');
|
||
|
|
}
|
||
|
|
listIdentities(): Observable<ExternalIdentity[]> {
|
||
|
|
return this.failList ? throwError(() => new Error('boom')) : of(this.linked);
|
||
|
|
}
|
||
|
|
unlink(provider: ExternalIdentityProvider): Observable<void> {
|
||
|
|
this.unlinkCalls.push(provider);
|
||
|
|
return of(void 0);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function make(gateway: FakeGateway): AccountIdentitiesComponent {
|
||
|
|
TestBed.configureTestingModule({
|
||
|
|
providers: [{ provide: SOCIAL_IDENTITY_GATEWAY, useValue: gateway }],
|
||
|
|
});
|
||
|
|
return TestBed.createComponent(AccountIdentitiesComponent).componentInstance;
|
||
|
|
}
|
||
|
|
|
||
|
|
describe('AccountIdentitiesComponent', () => {
|
||
|
|
it('lists the linked identities and reports ready', () => {
|
||
|
|
const g = new FakeGateway();
|
||
|
|
g.linked = [identity('telegram'), identity('vk_id')];
|
||
|
|
const c = make(g);
|
||
|
|
|
||
|
|
expect(c.state()).toBe('ready');
|
||
|
|
expect(c.identities().map(i => i.provider)).toEqual(['telegram', 'vk_id']);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('offers only the OAuth providers that are not already linked', () => {
|
||
|
|
const g = new FakeGateway();
|
||
|
|
g.linked = [identity('vk_id')]; // vk linked, yandex not
|
||
|
|
const c = make(g);
|
||
|
|
|
||
|
|
expect(c.linkable()).toEqual(['yandex']);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('refuses to unlink the last remaining identity', () => {
|
||
|
|
const g = new FakeGateway();
|
||
|
|
g.linked = [identity('telegram')];
|
||
|
|
const c = make(g);
|
||
|
|
|
||
|
|
expect(c.isLastIdentity()).toBe(true);
|
||
|
|
c.unlink('telegram');
|
||
|
|
|
||
|
|
expect(g.unlinkCalls).toEqual([]);
|
||
|
|
expect(c.identities().length).toBe(1);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('unlinks a provider when more than one is linked', () => {
|
||
|
|
const g = new FakeGateway();
|
||
|
|
g.linked = [identity('telegram'), identity('vk_id')];
|
||
|
|
const c = make(g);
|
||
|
|
|
||
|
|
c.unlink('vk_id');
|
||
|
|
|
||
|
|
expect(g.unlinkCalls).toEqual(['vk_id']);
|
||
|
|
expect(c.identities().map(i => i.provider)).toEqual(['telegram']);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('surfaces a load failure instead of showing an empty account', () => {
|
||
|
|
const g = new FakeGateway();
|
||
|
|
g.failList = true;
|
||
|
|
const c = make(g);
|
||
|
|
|
||
|
|
expect(c.state()).toBe('error');
|
||
|
|
});
|
||
|
|
});
|