Skip to content

Commit ac30e8f

Browse files
committed
Tests for popup
1 parent 8d4ccef commit ac30e8f

File tree

3 files changed

+195
-16
lines changed

3 files changed

+195
-16
lines changed

packages-exp/auth-exp/src/core/providers/oauth.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import * as externs from '@firebase/auth-types-exp';
1919

2020
import { AuthErrorCode } from '../errors';
21+
import { debugFail } from '../util/assert';
2122

2223
export interface CustomParameters {
2324
[key: string]: string;
@@ -35,19 +36,19 @@ export class OAuthProvider implements externs.AuthProvider {
3536
private customParameters: CustomParameters = {};
3637
constructor(readonly providerId: externs.ProviderId) {}
3738
static credentialFromResult(
38-
userCredential: externs.UserCredential
39+
_userCredential: externs.UserCredential
3940
): externs.OAuthCredential | null {
40-
throw new Error('not implemented');
41+
debugFail('not implemented');
4142
}
42-
static credentialFromError(error: AuthErrorCode): externs.OAuthCredential | null {
43-
throw new Error('not implemented');
43+
static credentialFromError(_error: AuthErrorCode): externs.OAuthCredential | null {
44+
debugFail('not implemented');
4445
}
45-
static credentialFromJSON(json: object): externs.OAuthCredential {
46-
throw new Error('not implemented');
46+
static credentialFromJSON(_json: object): externs.OAuthCredential {
47+
debugFail('not implemented');
4748
}
4849

49-
credential(params: CredentialParameters): externs.OAuthCredential {
50-
throw new Error('no');
50+
credential(_params: CredentialParameters): externs.OAuthCredential {
51+
debugFail('not implemented');
5152
}
5253

5354
setDefaultLanguage(languageCode: string | null): void {
@@ -63,15 +64,15 @@ export class OAuthProvider implements externs.AuthProvider {
6364
return this.customParameters;
6465
}
6566

66-
addScope(scope: string) {
67+
addScope(scope: string): externs.AuthProvider {
6768
// If not already added, add scope to list.
6869
if (!this.scopes.includes(scope)) {
6970
this.scopes.push(scope);
7071
}
7172
return this;
7273
}
7374

74-
getScopes() {
75-
return this.scopes;
75+
getScopes(): string[] {
76+
return [...this.scopes];
7677
}
7778
}
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
/**
2+
* @license
3+
* Copyright 2020 Google LLC.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { expect, use } from 'chai';
19+
import * as sinon from 'sinon';
20+
import * as sinonChai from 'sinon-chai';
21+
22+
import { FirebaseError } from '@firebase/util';
23+
24+
import { _open, _uaGetter, AuthPopup } from './popup';
25+
26+
use(sinonChai);
27+
28+
describe('src/core/util/popup', () => {
29+
let windowOpenStub: sinon.SinonStub;
30+
let popupStub: sinon.SinonStubbedInstance<Window>;
31+
32+
function setUA(ua: string): void {
33+
sinon.stub(_uaGetter, 'getUA').returns(ua);
34+
}
35+
36+
function windowTarget(): string {
37+
return windowOpenStub.firstCall.args[1];
38+
}
39+
40+
function windowURL(): string {
41+
return windowOpenStub.firstCall.args[0];
42+
}
43+
44+
function windowOptions(): string {
45+
return windowOpenStub.firstCall.args[2];
46+
}
47+
48+
beforeEach(() => {
49+
windowOpenStub = sinon.stub(window, 'open');
50+
popupStub = sinon.stub({
51+
focus: () => {},
52+
close: () => {},
53+
} as unknown as Window);
54+
windowOpenStub.returns(popupStub);
55+
});
56+
57+
afterEach(() => {
58+
sinon.restore();
59+
});
60+
61+
it('sets target to name param if not chrome UA', () => {
62+
setUA('notchrome');
63+
_open('appName', 'url', 'name');
64+
expect(windowTarget()).to.eq('name');
65+
});
66+
67+
it('sets target to _blank if on chrome IOS', () => {
68+
setUA('crios/');
69+
_open('appName', 'url', 'name');
70+
expect(windowTarget()).to.eq('_blank');
71+
});
72+
73+
it('sets the firefox url to a default if not provided', () => {
74+
setUA('firefox/');
75+
_open('appName');
76+
expect(windowURL()).to.eq('http://localhost');
77+
});
78+
79+
it('sets the firefox url to the value provided', () => {
80+
setUA('firefox/');
81+
_open('appName', 'url');
82+
expect(windowURL()).to.eq('url');
83+
});
84+
85+
it('sets non-firefox url to empty if not provided', () => {
86+
setUA('not-ff/');
87+
_open('appName');
88+
expect(windowURL()).to.eq('');
89+
});
90+
91+
it('sets non-firefox url to url if not provided', () => {
92+
setUA('not-ff/');
93+
_open('appName', 'url');
94+
expect(windowURL()).to.eq('url');
95+
});
96+
97+
it('sets scrollbars to yes in popup', () => {
98+
setUA('firefox/');
99+
_open('appName');
100+
expect(windowOptions()).to.include('scrollbars=yes');
101+
});
102+
103+
it('errors if the popup is blocked', () => {
104+
setUA('');
105+
windowOpenStub.returns(undefined);
106+
expect(() => _open('appName')).to.throw(FirebaseError, 'auth/popup-blocked');
107+
});
108+
109+
it('builds the proper options string', () => {
110+
const screen = window.screen;
111+
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
112+
(window as any).sreen = {
113+
availHeight: 1000,
114+
availWidth: 2000,
115+
};
116+
117+
setUA('');
118+
_open('appName');
119+
const options = windowOptions().split(',').filter(s => !!s).map(prop => prop.split('='))
120+
.reduce<Record<string, string>>((accum, [prop, val]) => {
121+
accum[prop] = val;
122+
return accum;
123+
}, {});
124+
125+
expect(options).to.eql({
126+
location: 'yes',
127+
resizable: 'yes',
128+
statusbar: 'yes',
129+
toolbar: 'no',
130+
width: '500',
131+
height: '600',
132+
top: '0',
133+
left: '0',
134+
});
135+
136+
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
137+
(window as any).screen = screen;
138+
});
139+
140+
it('calls focus on the new popup', () => {
141+
setUA('');
142+
_open('appName');
143+
expect(popupStub.focus).to.have.been.called;
144+
});
145+
146+
it('does not fail if window.focus errors', () => {
147+
popupStub.focus.throws(new Error('lol no'));
148+
setUA('');
149+
expect(() => _open('appName')).not.to.throw(Error);
150+
});
151+
152+
context('resulting popup object', () => {
153+
let authPopup: AuthPopup;
154+
beforeEach(() => {
155+
setUA('');
156+
authPopup = _open('appName');
157+
});
158+
159+
it('has a window object', () => {
160+
expect(authPopup.window).to.eq(popupStub);
161+
});
162+
163+
it('calls through to the popup close', () => {
164+
authPopup.close();
165+
expect(popupStub.close).to.have.been.called;
166+
});
167+
168+
it('close() does not error if underlying call errors', () => {
169+
popupStub.close.throws(new Error('not this time'));
170+
expect(() => authPopup.close()).not.to.throw(Error);
171+
});
172+
});
173+
});

packages-exp/auth-exp/src/core/util/popup.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* @license
3-
* Copyright 2019 Google Inc.
3+
* Copyright 2020 Google LLC.
44
*
55
* Licensed under the Apache License, Version 2.0 (the "License");
66
* you may not use this file except in compliance with the License.
@@ -35,17 +35,22 @@ const FIREFOX_UA = 'firefox/';
3535
const FIREFOX_EMPTY_URL = 'http://localhost';
3636

3737
export class AuthPopup {
38-
public associatedEvent: string | null = null;
38+
associatedEvent: string | null = null;
3939

40-
constructor(public readonly window: Window) {}
40+
constructor(readonly window: Window) {}
4141

42-
close() {
42+
close(): void {
4343
try {
4444
this.window.close();
4545
} catch (e) {}
4646
}
4747
}
4848

49+
/** Wrapper so that we can stub the UA in tests */
50+
export const _uaGetter = {
51+
getUA,
52+
};
53+
4954
export function _open(
5055
appName: string,
5156
url?: string,
@@ -70,7 +75,7 @@ export function _open(
7075

7176
// Chrome iOS 7 and 8 is returning an undefined popup win when target is
7277
// specified, even though the popup is not necessarily blocked.
73-
const ua = getUA().toLowerCase();
78+
const ua = _uaGetter.getUA().toLowerCase();
7479

7580
if (name) {
7681
target = ua.includes(CHROME_IOS_UA) ? '_blank' : name;

0 commit comments

Comments
 (0)