Skip to content

Commit 8879d37

Browse files
authored
Merge d28343d into c46e3b9
2 parents c46e3b9 + d28343d commit 8879d37

File tree

5 files changed

+454
-0
lines changed

5 files changed

+454
-0
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/**
2+
* @license
3+
* Copyright 2019 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 * as externs from '@firebase/auth-types-exp';
19+
20+
import { AuthErrorCode } from '../errors';
21+
import { debugFail } from '../util/assert';
22+
23+
export type CustomParameters = Record<string, string>;
24+
25+
interface CredentialParameters {
26+
idToken?: string;
27+
accessToken?: string;
28+
rawNonce?: string;
29+
}
30+
31+
export class OAuthProvider implements externs.AuthProvider {
32+
defaultLanguageCode: string | null = null;
33+
private scopes: string[] = [];
34+
private customParameters: CustomParameters = {};
35+
private constructor(readonly providerId: externs.ProviderId) {}
36+
static credentialFromResult(
37+
_userCredential: externs.UserCredential
38+
): externs.OAuthCredential | null {
39+
debugFail('not implemented');
40+
}
41+
static credentialFromError(
42+
_error: AuthErrorCode
43+
): externs.OAuthCredential | null {
44+
debugFail('not implemented');
45+
}
46+
static credentialFromJSON(_json: object): externs.OAuthCredential {
47+
debugFail('not implemented');
48+
}
49+
50+
credential(_params: CredentialParameters): externs.OAuthCredential {
51+
debugFail('not implemented');
52+
}
53+
54+
setDefaultLanguage(languageCode: string | null): void {
55+
this.defaultLanguageCode = languageCode;
56+
}
57+
58+
setCustomParameters(
59+
customOAuthParameters: CustomParameters
60+
): externs.AuthProvider {
61+
this.customParameters = customOAuthParameters;
62+
return this;
63+
}
64+
65+
getCustomParameters(): CustomParameters {
66+
return this.customParameters;
67+
}
68+
69+
addScope(scope: string): externs.AuthProvider {
70+
// If not already added, add scope to list.
71+
if (!this.scopes.includes(scope)) {
72+
this.scopes.push(scope);
73+
}
74+
return this;
75+
}
76+
77+
getScopes(): string[] {
78+
return [...this.scopes];
79+
}
80+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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+
const CHROME_IOS_UA = 'crios/';
19+
const FIREFOX_UA = 'firefox/';
20+
const IPHONE_UA = 'iphone';
21+
const IPOD_UA = 'ipod';
22+
const IPAD_UA = 'ipad';
23+
24+
interface NavigatorStandalone extends Navigator {
25+
standalone?: unknown;
26+
}
27+
28+
export function _isFirefox(ua: string): boolean {
29+
return ua.includes(FIREFOX_UA);
30+
}
31+
32+
export function _isChromeIOS(ua: string): boolean {
33+
return ua.includes(CHROME_IOS_UA);
34+
}
35+
36+
export function _isIOS(ua: string): boolean {
37+
return ua.includes(IPHONE_UA) || ua.includes(IPOD_UA) || ua.includes(IPAD_UA);
38+
}
39+
40+
export function _isIOSStandalone(ua: string): boolean {
41+
return _isIOS(ua) && !!(window.navigator as NavigatorStandalone)?.standalone;
42+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
export function _generateEventId(prefix?: string): string {
19+
return `${prefix ? prefix : ''}${Math.floor(Math.random() * 1000000000)}`;
20+
}
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
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+
import * as utils from '@firebase/util';
24+
25+
import { _open, AuthPopup } from './popup';
26+
27+
use(sinonChai);
28+
29+
describe('src/core/util/popup', () => {
30+
let windowOpenStub: sinon.SinonStub;
31+
let popupStub: sinon.SinonStubbedInstance<Window>;
32+
33+
function setUA(ua: string): void {
34+
sinon.stub(utils, 'getUA').returns(ua);
35+
}
36+
37+
function windowTarget(): string {
38+
return windowOpenStub.firstCall.args[1];
39+
}
40+
41+
function windowURL(): string {
42+
return windowOpenStub.firstCall.args[0];
43+
}
44+
45+
function windowOptions(): string {
46+
return windowOpenStub.firstCall.args[2];
47+
}
48+
49+
beforeEach(() => {
50+
windowOpenStub = sinon.stub(window, 'open');
51+
popupStub = sinon.stub(({
52+
focus: () => {},
53+
close: () => {}
54+
} as unknown) as Window);
55+
windowOpenStub.returns(popupStub);
56+
});
57+
58+
afterEach(() => {
59+
sinon.restore();
60+
});
61+
62+
it('sets target to name param if not chrome UA', () => {
63+
setUA('notchrome');
64+
_open('appName', 'url', 'name');
65+
expect(windowTarget()).to.eq('name');
66+
});
67+
68+
it('sets target to _blank if on chrome IOS', () => {
69+
setUA('crios/');
70+
_open('appName', 'url', 'name');
71+
expect(windowTarget()).to.eq('_blank');
72+
});
73+
74+
it('sets the firefox url to a default if not provided', () => {
75+
setUA('firefox/');
76+
_open('appName');
77+
expect(windowURL()).to.eq('http://localhost');
78+
});
79+
80+
it('sets the firefox url to the value provided', () => {
81+
setUA('firefox/');
82+
_open('appName', 'url');
83+
expect(windowURL()).to.eq('url');
84+
});
85+
86+
it('sets non-firefox url to empty if not provided', () => {
87+
setUA('not-ff/');
88+
_open('appName');
89+
expect(windowURL()).to.eq('');
90+
});
91+
92+
it('sets non-firefox url to url if not provided', () => {
93+
setUA('not-ff/');
94+
_open('appName', 'url');
95+
expect(windowURL()).to.eq('url');
96+
});
97+
98+
it('sets scrollbars to yes in popup', () => {
99+
setUA('firefox/');
100+
_open('appName');
101+
expect(windowOptions()).to.include('scrollbars=yes');
102+
});
103+
104+
it('errors if the popup is blocked', () => {
105+
setUA('');
106+
windowOpenStub.returns(undefined);
107+
expect(() => _open('appName')).to.throw(
108+
FirebaseError,
109+
'auth/popup-blocked'
110+
);
111+
});
112+
113+
it('builds the proper options string', () => {
114+
const screen = window.screen;
115+
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
116+
(window as any).sreen = {
117+
availHeight: 1000,
118+
availWidth: 2000
119+
};
120+
121+
setUA('');
122+
_open('appName');
123+
const options = windowOptions()
124+
.split(',')
125+
.filter(s => !!s)
126+
.map(prop => prop.split('='))
127+
.reduce<Record<string, string>>((accum, [prop, val]) => {
128+
accum[prop] = val;
129+
return accum;
130+
}, {});
131+
132+
expect(options).to.eql({
133+
location: 'yes',
134+
resizable: 'yes',
135+
statusbar: 'yes',
136+
toolbar: 'no',
137+
width: '500',
138+
height: '600',
139+
top: '0',
140+
left: '0'
141+
});
142+
143+
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
144+
(window as any).screen = screen;
145+
});
146+
147+
it('calls focus on the new popup', () => {
148+
setUA('');
149+
_open('appName');
150+
expect(popupStub.focus).to.have.been.called;
151+
});
152+
153+
it('does not fail if window.focus errors', () => {
154+
popupStub.focus.throws(new Error('lol no'));
155+
setUA('');
156+
expect(() => _open('appName')).not.to.throw(Error);
157+
});
158+
159+
context('resulting popup object', () => {
160+
let authPopup: AuthPopup;
161+
beforeEach(() => {
162+
setUA('');
163+
authPopup = _open('appName');
164+
});
165+
166+
it('has a window object', () => {
167+
expect(authPopup.window).to.eq(popupStub);
168+
});
169+
170+
it('calls through to the popup close', () => {
171+
authPopup.close();
172+
expect(popupStub.close).to.have.been.called;
173+
});
174+
175+
it('close() does not error if underlying call errors', () => {
176+
popupStub.close.throws(new Error('not this time'));
177+
expect(() => authPopup.close()).not.to.throw(Error);
178+
});
179+
});
180+
});

0 commit comments

Comments
 (0)