diff --git a/packages/app/e2e/helpers.js b/packages/app/e2e/helpers.js new file mode 100644 index 0000000000..57e4ea3d90 --- /dev/null +++ b/packages/app/e2e/helpers.js @@ -0,0 +1,11 @@ +exports.getE2eTestProject = function getE2eTestProject() { + return 'react-native-firebase-testing'; +}; + +exports.getE2eEmulatorHost = function getE2eEmulatorHost() { + // Note that in most package implementations involving the emulator, we re-write + // localhost and 127.0.0.1 on Android to 10.0.2.2 (the Android emulator host interface) + // But this specific code is executing in the host context even during E2E test. + // So no re-write is necessary here. + return 'localhost'; +}; diff --git a/packages/auth/e2e/auth.e2e.js b/packages/auth/e2e/auth.e2e.js index 999f504382..fe764b8281 100644 --- a/packages/auth/e2e/auth.e2e.js +++ b/packages/auth/e2e/auth.e2e.js @@ -15,7 +15,31 @@ * */ +const TEST_EMAIL = 'test@example.com'; +const TEST_PASS = 'test1234'; + +const DISABLED_EMAIL = 'disabled@example.com'; +const DISABLED_PASS = 'test1234'; + +const { clearAllUsers, disableUser, getLastOob, resetPassword } = require('./helpers'); + describe('auth()', () => { + before(async () => { + await clearAllUsers(); + await firebase.auth().createUserWithEmailAndPassword(TEST_EMAIL, TEST_PASS); + const disabledUserCredential = await firebase + .auth() + .createUserWithEmailAndPassword(DISABLED_EMAIL, DISABLED_PASS); + await disableUser(disabledUserCredential.user.uid); + }); + + beforeEach(async () => { + if (firebase.auth().currentUser) { + await firebase.auth().signOut(); + await Utils.sleep(50); + } + }); + describe('namespace', () => { it('accessible from firebase.app()', () => { const app = firebase.app(); @@ -39,6 +63,7 @@ describe('auth()', () => { }); describe('applyActionCode()', () => { + // Needs a different setup to work against the auth emulator xit('works as expected', async () => { await firebase .auth() @@ -106,18 +131,8 @@ describe('auth()', () => { }); }); - describe('verifyPasswordResetCode()', () => { - it('errors on invalid code', async () => { - try { - await firebase.auth().verifyPasswordResetCode('fooby shooby dooby'); - } catch (e) { - e.message.should.containEql('code is invalid'); - } - }); - }); - describe('confirmPasswordReset()', () => { - it('errors on invalid code', async () => { + it('errors on invalid code via API', async () => { try { await firebase.auth().confirmPasswordReset('fooby shooby dooby', 'passwordthing'); } catch (e) { @@ -127,16 +142,14 @@ describe('auth()', () => { }); describe('signInWithCustomToken()', () => { - it('signs in with a admin sdk created custom auth token', async () => { - const email = 'test@test.com'; - const pass = 'test1234'; - + // Needs a different setup when running against the emulator + xit('signs in with a admin sdk created custom auth token', async () => { const successCb = currentUserCredential => { const currentUser = currentUserCredential.user; currentUser.should.be.an.Object(); currentUser.uid.should.be.a.String(); currentUser.toJSON().should.be.an.Object(); - currentUser.toJSON().email.should.eql(email); + currentUser.toJSON().email.should.eql(TEST_EMAIL); currentUser.isAnonymous.should.equal(false); currentUser.providerId.should.equal('firebase'); currentUser.should.equal(firebase.auth().currentUser); @@ -150,7 +163,7 @@ describe('auth()', () => { const user = await firebase .auth() - .signInWithEmailAndPassword(email, pass) + .signInWithEmailAndPassword(TEST_EMAIL, TEST_PASS) .then(successCb); const IdToken = await firebase.auth().currentUser.getIdToken(); @@ -162,7 +175,7 @@ describe('auth()', () => { await firebase.auth().signInWithCustomToken(token); - firebase.auth().currentUser.email.should.equal('test@test.com'); + firebase.auth().currentUser.email.should.equal(TEST_EMAIL); }); }); @@ -540,15 +553,12 @@ describe('auth()', () => { describe('signInWithEmailAndPassword()', () => { it('it should login with email and password', () => { - const email = 'test@test.com'; - const pass = 'test1234'; - const successCb = currentUserCredential => { const currentUser = currentUserCredential.user; currentUser.should.be.an.Object(); currentUser.uid.should.be.a.String(); currentUser.toJSON().should.be.an.Object(); - currentUser.toJSON().email.should.eql(email); + currentUser.toJSON().email.should.eql(TEST_EMAIL); currentUser.isAnonymous.should.equal(false); currentUser.providerId.should.equal('firebase'); currentUser.should.equal(firebase.auth().currentUser); @@ -562,14 +572,11 @@ describe('auth()', () => { return firebase .auth() - .signInWithEmailAndPassword(email, pass) + .signInWithEmailAndPassword(TEST_EMAIL, TEST_PASS) .then(successCb); }); it('it should error on login if user is disabled', () => { - const email = 'disabled@account.com'; - const pass = 'test1234'; - const successCb = () => Promise.reject(new Error('Did not error.')); const failureCb = error => { @@ -580,15 +587,12 @@ describe('auth()', () => { return firebase .auth() - .signInWithEmailAndPassword(email, pass) + .signInWithEmailAndPassword(DISABLED_EMAIL, DISABLED_PASS) .then(successCb) .catch(failureCb); }); it('it should error on login if password incorrect', () => { - const email = 'test@test.com'; - const pass = 'test1234666'; - const successCb = () => Promise.reject(new Error('Did not error.')); const failureCb = error => { @@ -601,7 +605,7 @@ describe('auth()', () => { return firebase .auth() - .signInWithEmailAndPassword(email, pass) + .signInWithEmailAndPassword(TEST_EMAIL, TEST_PASS + '666') .then(successCb) .catch(failureCb); }); @@ -631,14 +635,14 @@ describe('auth()', () => { describe('signInWithCredential()', () => { it('it should login with email and password', () => { - const credential = firebase.auth.EmailAuthProvider.credential('test@test.com', 'test1234'); + const credential = firebase.auth.EmailAuthProvider.credential(TEST_EMAIL, TEST_PASS); const successCb = currentUserCredential => { const currentUser = currentUserCredential.user; currentUser.should.be.an.Object(); currentUser.uid.should.be.a.String(); currentUser.toJSON().should.be.an.Object(); - currentUser.toJSON().email.should.eql('test@test.com'); + currentUser.toJSON().email.should.eql(TEST_EMAIL); currentUser.isAnonymous.should.equal(false); currentUser.providerId.should.equal('firebase'); currentUser.should.equal(firebase.auth().currentUser); @@ -657,10 +661,7 @@ describe('auth()', () => { }); it('it should error on login if user is disabled', () => { - const credential = firebase.auth.EmailAuthProvider.credential( - 'disabled@account.com', - 'test1234', - ); + const credential = firebase.auth.EmailAuthProvider.credential(DISABLED_EMAIL, DISABLED_PASS); const successCb = () => Promise.reject(new Error('Did not error.')); @@ -678,7 +679,7 @@ describe('auth()', () => { }); it('it should error on login if password incorrect', () => { - const credential = firebase.auth.EmailAuthProvider.credential('test@test.com', 'test1234666'); + const credential = firebase.auth.EmailAuthProvider.credential(TEST_EMAIL, TEST_PASS + '666'); const successCb = () => Promise.reject(new Error('Did not error.')); @@ -770,9 +771,6 @@ describe('auth()', () => { }); it('it should error on create if email in use', () => { - const email = 'test@test.com'; - const pass = 'test123456789'; - const successCb = () => Promise.reject(new Error('Did not error.')); const failureCb = error => { @@ -783,7 +781,7 @@ describe('auth()', () => { return firebase .auth() - .createUserWithEmailAndPassword(email, pass) + .createUserWithEmailAndPassword(TEST_EMAIL, TEST_PASS) .then(successCb) .catch(failureCb); }); @@ -824,7 +822,7 @@ describe('auth()', () => { return firebase .auth() - .fetchSignInMethodsForEmail('test@test.com') + .fetchSignInMethodsForEmail(TEST_EMAIL) .then(successCb) .catch(failureCb); })); @@ -982,11 +980,97 @@ describe('auth()', () => { try { await firebase.auth().sendPasswordResetEmail(email); + } catch (error) { + throw new Error('sendPasswordResetEmail() caused an error', error); + } finally { await firebase.auth().currentUser.delete(); + } + }); + + it('should verify with valid code', async () => { + // FIXME Fails on android against auth emulator with: + // com.google.firebase.FirebaseException: An internal error has occurred. + if (device.getPlatform() === 'ios') { + const random = Utils.randString(12, '#a'); + const email = `${random}@${random}.com`; + const userCredential = await firebase.auth().createUserWithEmailAndPassword(email, random); + userCredential.user.emailVerified.should.equal(false); + firebase.auth().currentUser.email.should.equal(email); + firebase.auth().currentUser.emailVerified.should.equal(false); + + try { + await firebase.auth().sendPasswordResetEmail(email); + const { oobCode } = await getLastOob(email); + await firebase.auth().verifyPasswordResetCode(oobCode); + } catch (error) { + throw new Error('sendPasswordResetEmail() caused an error', error); + } finally { + await firebase.auth().currentUser.delete(); + } + } + }); + + it('should fail to verify with invalid code', async () => { + const random = Utils.randString(12, '#a'); + const email = `${random}@${random}.com`; + const userCredential = await firebase.auth().createUserWithEmailAndPassword(email, random); + userCredential.user.emailVerified.should.equal(false); + firebase.auth().currentUser.email.should.equal(email); + firebase.auth().currentUser.emailVerified.should.equal(false); + + try { + await firebase.auth().sendPasswordResetEmail(email); + const { oobCode } = await getLastOob(email); + await firebase.auth().verifyPasswordResetCode(oobCode + 'badcode'); + throw new Error('Invalid code should throw an error'); + } catch (error) { + error.message.should.containEql('[auth/invalid-action-code]'); + } finally { + await firebase.auth().currentUser.delete(); + } + }); + + it('should change password correctly OOB', async () => { + const random = Utils.randString(12, '#a'); + const email = `${random}@${random}.com`; + const userCredential = await firebase.auth().createUserWithEmailAndPassword(email, random); + userCredential.user.emailVerified.should.equal(false); + firebase.auth().currentUser.email.should.equal(email); + firebase.auth().currentUser.emailVerified.should.equal(false); + + try { + await firebase.auth().sendPasswordResetEmail(email); + const { oobCode } = await getLastOob(email); + await resetPassword(oobCode, 'testNewPassword'); + await firebase.auth().signOut(); + await Utils.sleep(50); + await firebase.auth().signInWithEmailAndPassword(email, 'testNewPassword'); } catch (error) { - // Reject + throw new Error('sendPasswordResetEmail() caused an error', error); + } finally { await firebase.auth().currentUser.delete(); + } + }); + + it('should change password correctly via API', async () => { + const random = Utils.randString(12, '#a'); + const email = `${random}@${random}.com`; + const userCredential = await firebase.auth().createUserWithEmailAndPassword(email, random); + userCredential.user.emailVerified.should.equal(false); + firebase.auth().currentUser.email.should.equal(email); + firebase.auth().currentUser.emailVerified.should.equal(false); + + try { + await firebase.auth().sendPasswordResetEmail(email); + const { oobCode } = await getLastOob(email); + await firebase.auth().confirmPasswordReset(oobCode, 'testNewPassword'); + await firebase.auth().signOut(); + await Utils.sleep(50); + await firebase.auth().signInWithEmailAndPassword(email, 'testNewPassword'); + } catch (error) { throw new Error('sendPasswordResetEmail() caused an error', error); + } finally { + await firebase.auth().currentUser.delete(); } }); }); diff --git a/packages/auth/e2e/emailLink.e2e.js b/packages/auth/e2e/emailLink.e2e.js index 3332d601dc..bc2ced2b7d 100644 --- a/packages/auth/e2e/emailLink.e2e.js +++ b/packages/auth/e2e/emailLink.e2e.js @@ -1,3 +1,5 @@ +const { getLastOob, signInUser } = require('./helpers'); + describe('auth() -> emailLink Provider', () => { beforeEach(async () => { if (firebase.auth().currentUser) { @@ -26,6 +28,30 @@ describe('auth() -> emailLink Provider', () => { await firebase.auth().sendSignInLinkToEmail(email, actionCodeSettings); }); + it('sign in via email works', async () => { + const random = Utils.randString(12, '#aa'); + const email = `${random}@${random}.com`; + const continueUrl = 'http://localhost:1337/authLinkFoo?bar=' + random; + const actionCodeSettings = { + url: continueUrl, + handleCodeInApp: true, + iOS: { + bundleId: 'com.testing', + }, + android: { + packageName: 'com.testing', + installApp: true, + minimumVersion: '12', + }, + }; + await firebase.auth().sendSignInLinkToEmail(email, actionCodeSettings); + const oobInfo = await getLastOob(email); + oobInfo.oobLink.should.containEql(encodeURIComponent(continueUrl)); + const signInResponse = await signInUser(oobInfo.oobLink); + signInResponse.should.containEql(continueUrl); + signInResponse.should.containEql(oobInfo.oobCode); + }); + xit('should send email with defaults', async () => { const random = Utils.randString(12, '#aA'); const email = `${random}@${random}.com`; diff --git a/packages/auth/e2e/helpers.js b/packages/auth/e2e/helpers.js new file mode 100644 index 0000000000..536cd17c86 --- /dev/null +++ b/packages/auth/e2e/helpers.js @@ -0,0 +1,191 @@ +/* eslint-disable no-console */ +const { getE2eTestProject, getE2eEmulatorHost } = require('../../app/e2e/helpers'); +const http = require('http'); + +// Call HTTP REST API URL and return JSON response parsed into object +const callRestApi = async function callRestAPI(url, rawResult = false) { + // const TAG = 'auth::e2e:helpers:callRestApi - '; + return await new Promise((resolve, reject) => { + // console.log(TAG + 'making request'); + const req = http.get(url, response => { + // console.log(TAG + 'callback'); + let data = ''; + response.on('data', chunk => { + // console.log(TAG + 'request callback response data callback'); + // console.log(TAG + 'data event, got chunk: ' + chunk); + data += chunk; + }); + response.on('end', () => { + // console.log(TAG + 'request callback response end callback'); + if (rawResult) { + resolve(data); + } else { + resolve(JSON.parse(data)); + } + }); + }); + req.on('error', error => reject(error)); + }); +}; + +exports.getRandomPhoneNumber = function getRandomPhoneNumber() { + return '+593' + Utils.randString(9, '#19'); +}; + +exports.clearAllUsers = async function clearAllUsers() { + // console.log('auth::helpers::clearAllUsers'); + try { + const deleteOptions = { + method: 'DELETE', + headers: { + // Undocumented, but necessary - from Emulator UI network requests + Authorization: 'Bearer owner', + }, + port: 9099, + host: getE2eEmulatorHost(), + path: '/emulator/v1/projects/' + getE2eTestProject() + '/accounts', + }; + // console.log('request: ' + JSON.stringify(deleteOptions)); + await new Promise((resolve, reject) => { + const req = http.request(deleteOptions); + req.on('error', error => reject(error)); + req.end(resolve()); + }); + } catch (e) { + console.error('Unable to wipe auth:', e); + throw e; + } +}; + +exports.disableUser = async function disableUser(userId) { + // console.log('auth::helpers::disableUser on userId: ' + userId); + const reqBody = JSON.stringify({ disableUser: true, localId: userId }); + try { + const postOptions = { + method: 'POST', + headers: { + // Undocumented, but necessary - from Emulator UI network requests + Authorization: 'Bearer owner', + 'Content-Type': 'application/json', + 'Content-Length': reqBody.length, + }, + port: 9099, + host: getE2eEmulatorHost(), + path: '/identitytoolkit.googleapis.com/v1/accounts:update', + }; + // console.log('request: ' + JSON.stringify(postOptions)); + await new Promise((resolve, reject) => { + const req = http.request(postOptions); + req.on('error', error => reject(error)); + req.write(reqBody); + req.end(resolve()); + }); + } catch (e) { + console.error('Unable to update user:', e); + throw e; + } +}; + +exports.getLastSmsCode = async function getLastSmsCode(specificPhone) { + let lastSmsCode = null; + try { + // console.log('auth::e2e:helpers:getLastSmsCode - start'); + const getSmsCodesUrl = + 'http://' + + getE2eEmulatorHost() + + ':9099/emulator/v1/projects/' + + getE2eTestProject() + + '/verificationCodes'; + + const responseData = await callRestApi(getSmsCodesUrl); + + // Process the codes, the last one in the array is the one... + // console.log('getLastSmsCode got ', JSON.stringify(responseData, null, 2)); + const codes = responseData ? responseData.verificationCodes : undefined; + if (codes && codes.length > 0) { + if (specificPhone) { + // roll through backwards (to get last valid code) searching for the specific phone + for (let i = codes.length - 1; i >= 0 && !lastSmsCode; i--) { + const codeBlock = codes[i]; + if (codeBlock.phoneNumber === specificPhone) { + lastSmsCode = codeBlock.code; + } + } + } else { + lastSmsCode = codes[codes.length - 1].code; + } + } else { + throw new Error('There were no unused verification codes'); + } + } catch (e) { + console.error('Unable to get SMS Verification codes', e); + throw e; + } + // console.log('getLastSmsCode returning code: ' + lastSmsCode); + return lastSmsCode; +}; + +exports.getLastOob = async function getLastOob(specificEmail) { + let lastOob = null; + try { + // console.log('auth::e2e:helpers:getLastOob - start'); + const getOobCodesUrl = + 'http://' + + getE2eEmulatorHost() + + ':9099/emulator/v1/projects/' + + getE2eTestProject() + + '/oobCodes'; + + const responseData = await callRestApi(getOobCodesUrl); + + // Process the codes, the last one in the array is the one... + // console.log('getLastOob got ', JSON.stringify(responseData, null, 2)); + const codes = responseData ? responseData.oobCodes : undefined; + if (codes && codes.length > 0) { + if (specificEmail) { + // roll through backwards (to get last valid code) searching for the specific email + for (let i = codes.length - 1; i >= 0 && !lastOob; i--) { + const codeBlock = codes[i]; + if (codeBlock.email === specificEmail) { + lastOob = codeBlock; + } + } + } else { + lastOob = codes[codes.length - 1]; + } + } else { + throw new Error('There were no unused OOB codes'); + } + } catch (e) { + console.error('Unable to get Email OOB codes', e); + throw e; + } + // console.log('getLastOob returning code: ' + JSON.stringify(lastOob, null, 2); + return lastOob; +}; + +exports.resetPassword = async function resetPassword(oobCode, newPassword) { + const resetPasswordUrl = + 'http://' + + getE2eEmulatorHost() + + ':9099/emulator/action?mode=resetPassword&lang=en&oobCode=' + + oobCode + + '&apiKey=fake-api-key&newPassword=' + + newPassword; + return await callRestApi(resetPasswordUrl); +}; + +exports.verifyEmail = async function verifyEmail(oobCode) { + const verifyEmailUrl = + 'http://' + + getE2eEmulatorHost() + + ':9099/emulator/action?mode=verifyEmail&lang=en&oobCode=' + + oobCode + + '&apiKey=fake-api-key'; + return await callRestApi(verifyEmailUrl); +}; + +// This URL comes from the Auth Emulator's oobCode blocks +exports.signInUser = async function signInUser(oobUrl) { + return await callRestApi(oobUrl, true); +}; diff --git a/packages/auth/e2e/phone.e2e.js b/packages/auth/e2e/phone.e2e.js index b45deca381..22e5695ed2 100644 --- a/packages/auth/e2e/phone.e2e.js +++ b/packages/auth/e2e/phone.e2e.js @@ -1,13 +1,16 @@ -const TEST_PHONE_A = '+447445255123'; -const TEST_CODE_A = '123456'; +// const TEST_EMAIL = 'test@test.com'; +// const TEST_PASS = 'test1234'; -// const TEST_PHONE_B = '+447445123457'; -// const TEST_CODE_B = '654321'; +const { clearAllUsers, getLastSmsCode, getRandomPhoneNumber } = require('./helpers'); describe('auth() => Phone', () => { before(async () => { + try { + await clearAllUsers(); + } catch (e) { + throw e; + } firebase.auth().settings.appVerificationDisabledForTesting = true; - await firebase.auth().settings.setAutoRetrievedSmsCodeForPhoneNumber(TEST_PHONE_A, TEST_CODE_A); await Utils.sleep(50); }); @@ -19,12 +22,14 @@ describe('auth() => Phone', () => { }); describe('signInWithPhoneNumber', () => { - xit('signs in with a valid code', async () => { - const confirmResult = await firebase.auth().signInWithPhoneNumber(TEST_PHONE_A); + it('signs in with a valid code', async () => { + const testPhone = await getRandomPhoneNumber(); + const confirmResult = await firebase.auth().signInWithPhoneNumber(testPhone); confirmResult.verificationId.should.be.a.String(); should.ok(confirmResult.verificationId.length, 'verificationId string should not be empty'); confirmResult.confirm.should.be.a.Function(); - const userCredential = await confirmResult.confirm(TEST_CODE_A); + const lastSmsCode = await getLastSmsCode(testPhone); + const userCredential = await confirmResult.confirm(lastSmsCode); userCredential.user.should.be.instanceOf(jet.require('packages/auth/lib/User')); // Broken check, phone number is undefined @@ -32,42 +37,46 @@ describe('auth() => Phone', () => { }); it('errors on invalid code', async () => { - const confirmResult = await firebase.auth().signInWithPhoneNumber(TEST_PHONE_A); + const testPhone = await getRandomPhoneNumber(); + const confirmResult = await firebase.auth().signInWithPhoneNumber(testPhone); confirmResult.verificationId.should.be.a.String(); should.ok(confirmResult.verificationId.length, 'verificationId string should not be empty'); confirmResult.confirm.should.be.a.Function(); - await confirmResult.confirm('666999').should.be.rejected(); + // Get the last SMS code just to make absolutely sure we don't accidentally use it + const lastSmsCode = await getLastSmsCode(testPhone); + await confirmResult + .confirm(lastSmsCode === '000000' ? '111111' : '000000') + .should.be.rejected(); // TODO test error code and message + + // If you don't consume the valid code, then it sticks around + await confirmResult.confirm(lastSmsCode); }); }); describe('verifyPhoneNumber', async () => { it('successfully verifies', async () => { - const TEST_PHONE_A = '+447445255123'; - const confirmResult = await firebase.auth().signInWithPhoneNumber(TEST_PHONE_A); - - await confirmResult.confirm(TEST_CODE_A); - await firebase.auth().verifyPhoneNumber(TEST_PHONE_A, false, false); + const testPhone = await getRandomPhoneNumber(); + const confirmResult = await firebase.auth().signInWithPhoneNumber(testPhone); + const lastSmsCode = await getLastSmsCode(testPhone); + await confirmResult.confirm(lastSmsCode); + await firebase.auth().verifyPhoneNumber(testPhone, false, false); }); it('uses the autoVerifyTimeout when a non boolean autoVerifyTimeoutOrForceResend is provided', async () => { - const TEST_PHONE_A = '+447445255123'; - const confirmResult = await firebase.auth().signInWithPhoneNumber(TEST_PHONE_A); - - await confirmResult.confirm(TEST_CODE_A); - await firebase.auth().verifyPhoneNumber(TEST_PHONE_A, 0, false); + const testPhone = await getRandomPhoneNumber(); + const confirmResult = await firebase.auth().signInWithPhoneNumber(testPhone); + const lastSmsCode = await getLastSmsCode(testPhone); + await confirmResult.confirm(lastSmsCode); + await firebase.auth().verifyPhoneNumber(testPhone, 0, false); }); it('throws an error with an invalid on event', async () => { - const TEST_PHONE_A = '+447445255123'; - const confirmResult = await firebase.auth().signInWithPhoneNumber(TEST_PHONE_A); - - await confirmResult.confirm(TEST_CODE_A); - + const testPhone = await getRandomPhoneNumber(); try { await firebase .auth() - .verifyPhoneNumber(TEST_PHONE_A) + .verifyPhoneNumber(testPhone) .on('example', () => {}); return Promise.reject(new Error('Did not throw Error.')); @@ -80,15 +89,11 @@ describe('auth() => Phone', () => { }); it('throws an error with an invalid observer event', async () => { - const TEST_PHONE_A = '+447445255123'; - const confirmResult = await firebase.auth().signInWithPhoneNumber(TEST_PHONE_A); - - await confirmResult.confirm(TEST_CODE_A); - + const testPhone = await getRandomPhoneNumber(); try { await firebase .auth() - .verifyPhoneNumber(TEST_PHONE_A) + .verifyPhoneNumber(testPhone) .on('state_changed', null, null, () => {}); return Promise.reject(new Error('Did not throw Error.')); @@ -101,45 +106,30 @@ describe('auth() => Phone', () => { }); it('successfully runs verification complete handler', async () => { - const TEST_PHONE_A = '+447445255123'; - const confirmResult = await firebase.auth().signInWithPhoneNumber(TEST_PHONE_A); - - await confirmResult.confirm(TEST_CODE_A); - + const testPhone = await getRandomPhoneNumber(); await firebase .auth() - .verifyPhoneNumber(TEST_PHONE_A) + .verifyPhoneNumber(testPhone) .then($ => $); return Promise.resolve(); }); - it('successfully runs and adds emiters', async () => { - const TEST_PHONE_A = '+447445255123'; - const confirmResult = await firebase.auth().signInWithPhoneNumber(TEST_PHONE_A); - - await confirmResult.confirm(TEST_CODE_A); - + it('successfully runs and adds emitters', async () => { + const testPhone = await getRandomPhoneNumber(); const obervserCb = () => {}; - const errorCb = () => {}; - const successCb = () => { return Promise.resolve(); }; await firebase .auth() - .verifyPhoneNumber(TEST_PHONE_A) + .verifyPhoneNumber(testPhone) .on('state_changed', obervserCb, errorCb, successCb, () => {}); }); it('catches an error and emits an error event', async () => { - const TEST_PHONE_A = '+447445255123'; - const confirmResult = await firebase.auth().signInWithPhoneNumber(TEST_PHONE_A); - - await confirmResult.confirm(TEST_CODE_A); - return firebase .auth() .verifyPhoneNumber('test') diff --git a/packages/auth/e2e/rnReload.e2e.js b/packages/auth/e2e/rnReload.e2e.js index 5ebfdb1623..bbb89ac014 100644 --- a/packages/auth/e2e/rnReload.e2e.js +++ b/packages/auth/e2e/rnReload.e2e.js @@ -1,4 +1,22 @@ +const TEST_EMAIL = 'test@test.com'; +const TEST_PASS = 'test1234'; + +const { clearAllUsers } = require('./helpers'); + describe('auth()', () => { + before(async () => { + try { + await clearAllUsers(); + } catch (e) { + throw e; + } + try { + await firebase.auth().createUserWithEmailAndPassword(TEST_EMAIL, TEST_PASS); + } catch (e) { + // they may already exist, that's fine + } + }); + beforeEach(async () => { if (firebase.auth().currentUser) { await firebase.auth().signOut(); @@ -39,15 +57,13 @@ describe('auth()', () => { // in with a different user then reloading await firebase.auth().signOut(); - const email = 'test@test.com'; - const pass = 'test1234'; - await firebase.auth().signInWithEmailAndPassword(email, pass); + await firebase.auth().signInWithEmailAndPassword(TEST_EMAIL, TEST_PASS); ({ currentUser } = firebase.auth()); currentUser.should.be.an.Object(); currentUser.uid.should.be.a.String(); currentUser.toJSON().should.be.an.Object(); - currentUser.toJSON().email.should.eql(email); + currentUser.toJSON().email.should.eql(TEST_EMAIL); currentUser.isAnonymous.should.equal(false); currentUser.providerId.should.equal('firebase'); currentUser.should.equal(firebase.auth().currentUser); @@ -60,7 +76,7 @@ describe('auth()', () => { currentUser.should.be.an.Object(); currentUser.uid.should.be.a.String(); currentUser.toJSON().should.be.an.Object(); - currentUser.toJSON().email.should.eql(email); + currentUser.toJSON().email.should.eql(TEST_EMAIL); currentUser.isAnonymous.should.equal(false); currentUser.providerId.should.equal('firebase'); currentUser.should.equal(firebase.auth().currentUser); diff --git a/packages/auth/e2e/user.e2e.js b/packages/auth/e2e/user.e2e.js index 9bb487583c..c59cff961e 100644 --- a/packages/auth/e2e/user.e2e.js +++ b/packages/auth/e2e/user.e2e.js @@ -1,4 +1,29 @@ +const TEST_EMAIL = 'test@test.com'; +const TEST_PASS = 'test1234'; + +const { + clearAllUsers, + getLastSmsCode, + getRandomPhoneNumber, + getLastOob, + verifyEmail, +} = require('./helpers'); + describe('auth().currentUser', () => { + before(async () => { + try { + await clearAllUsers(); + } catch (e) { + throw e; + } + firebase.auth().settings.appVerificationDisabledForTesting = true; + try { + await firebase.auth().createUserWithEmailAndPassword(TEST_EMAIL, TEST_PASS); + } catch (e) { + // they may already exist, that's fine + } + }); + beforeEach(async () => { if (firebase.auth().currentUser) { await firebase.auth().signOut(); @@ -57,6 +82,7 @@ describe('auth().currentUser', () => { }); describe('linkWithCredential()', () => { + // hanging against auth emulator? it('should link anonymous account <-> email account', async () => { const random = Utils.randString(12, '#aA'); const email = `${random}@${random}.com`; @@ -85,15 +111,12 @@ describe('auth().currentUser', () => { }); it('should error on link anon <-> email if email already exists', async () => { - const email = 'test@test.com'; - const pass = 'test1234'; - await firebase.auth().signInAnonymously(); const { currentUser } = firebase.auth(); // Test try { - const credential = firebase.auth.EmailAuthProvider.credential(email, pass); + const credential = firebase.auth.EmailAuthProvider.credential(TEST_EMAIL, TEST_PASS); await currentUser.linkWithCredential(credential); // Clean up @@ -315,15 +338,33 @@ describe('auth().currentUser', () => { try { await firebase.auth().currentUser.sendEmailVerification(); + } catch (error) { + return Promise.reject(new Error('sendEmailVerification() caused an error', error)); + } finally { await firebase.auth().currentUser.delete(); + } + + return Promise.resolve(); + }); + + it('should correctly report emailVerified status', async () => { + const random = Utils.randString(12, '#a'); + const email = `${random}@${random}.com`; + await firebase.auth().createUserWithEmailAndPassword(email, random); + firebase.auth().currentUser.email.should.equal(email); + firebase.auth().currentUser.emailVerified.should.equal(false); + + try { + await firebase.auth().currentUser.sendEmailVerification(); + const { oobCode } = await getLastOob(email); + await verifyEmail(oobCode); + firebase.auth().currentUser.emailVerified.should.equal(false); + await firebase.auth().currentUser.reload(); + firebase.auth().currentUser.emailVerified.should.equal(true); } catch (error) { - // Reject - try { - await firebase.auth().currentUser.delete(); - } catch (_) { - /* do nothing */ - } return Promise.reject(new Error('sendEmailVerification() caused an error', error)); + } finally { + await firebase.auth().currentUser.delete(); } return Promise.resolve(); @@ -340,18 +381,12 @@ describe('auth().currentUser', () => { try { await firebase.auth().currentUser.sendEmailVerification(actionCodeSettings); - await firebase.auth().currentUser.delete(); } catch (error) { - // Reject - try { - await firebase.auth().currentUser.delete(); - } catch (_) { - /* do nothing */ - } - return Promise.reject( - new Error('sendEmailVerification(actionCodeSettings) caused an error' + error.message), + new Error('sendEmailVerification(actionCodeSettings) error' + error.message), ); + } finally { + await firebase.auth().currentUser.delete(); } return Promise.resolve(); @@ -580,6 +615,8 @@ describe('auth().currentUser', () => { await firebase.auth().currentUser.delete(); }); + // FIXME ios+android failing with an internal error against auth emulator + // com.google.firebase.FirebaseException: An internal error has occurred. [ VERIFY_AND_CHANGE_EMAIL ] xit('should not error', async () => { const random = Utils.randString(12, '#aA'); const random2 = Utils.randString(12, '#aA'); @@ -589,12 +626,14 @@ describe('auth().currentUser', () => { try { await firebase.auth().createUserWithEmailAndPassword(email, random); await firebase.auth().currentUser.verifyBeforeUpdateEmail(updateEmail); - } catch (_) { + } catch (e) { return Promise.reject("'verifyBeforeUpdateEmail()' did not work"); } await firebase.auth().currentUser.delete(); }); + // FIXME ios+android failing with an internal error against auth emulator + // com.google.firebase.FirebaseException: An internal error has occurred. [ VERIFY_AND_CHANGE_EMAIL ] xit('should work with actionCodeSettings', async () => { const random = Utils.randString(12, '#aA'); const random2 = Utils.randString(12, '#aA'); @@ -610,7 +649,8 @@ describe('auth().currentUser', () => { } catch (error) { try { await firebase.auth().currentUser.delete(); - } catch (_) { + } catch (e) { + consle.log(e); /* do nothing */ } @@ -646,68 +686,65 @@ describe('auth().currentUser', () => { describe('updateEmail()', () => { it('should update the email address', async () => { - const random = Utils.randString(12, '#aA'); - const random2 = Utils.randString(12, '#aA'); + const random = Utils.randString(12, '#a'); + const random2 = Utils.randString(12, '#a'); const email = `${random}@${random}.com`; const email2 = `${random2}@${random2}.com`; // Setup await firebase.auth().createUserWithEmailAndPassword(email, random); - firebase - .auth() - .currentUser.email.toLowerCase() - .should.equal(email.toLowerCase()); + firebase.auth().currentUser.email.should.equal(email); // Update user email await firebase.auth().currentUser.updateEmail(email2); // Assertions - firebase - .auth() - .currentUser.email.toLowerCase() - .should.equal(email2.toLowerCase()); + firebase.auth().currentUser.email.should.equal(email2); // Clean up await firebase.auth().currentUser.delete(); }); }); - // TODO: Figure how to mock phone credentials on updating a phone number describe('updatePhoneNumber()', () => { - it('should update the profile', async () => { - // Create with initial number - const TEST_PHONE_A = '+447445255123'; - const TEST_CODE_A = '123456'; - - firebase.auth().settings.appVerificationDisabledForTesting = true; + it('should update the phone number', async () => { + const testPhone = await getRandomPhoneNumber(); + const confirmResult = await firebase.auth().signInWithPhoneNumber(testPhone); + const smsCode = await getLastSmsCode(testPhone); + await confirmResult.confirm(smsCode); - await firebase - .auth() - .settings.setAutoRetrievedSmsCodeForPhoneNumber(TEST_PHONE_A, TEST_CODE_A); - await Utils.sleep(50); + firebase.auth().currentUser.phoneNumber.should.equal(testPhone); - //Sign Out - if (firebase.auth().currentUser) { - await firebase.auth().signOut(); - await Utils.sleep(50); - } - - //Sign in number - const confirmResult = await firebase.auth().signInWithPhoneNumber(TEST_PHONE_A); - - await confirmResult.confirm(TEST_CODE_A); + const newPhone = await getRandomPhoneNumber(); + const newPhoneVerificationId = await new Promise((resolve, reject) => { + firebase + .auth() + .verifyPhoneNumber(newPhone) + .on('state_changed', phoneAuthSnapshot => { + if (phoneAuthSnapshot.error) { + reject(phoneAuthSnapshot.error); + } else { + resolve(phoneAuthSnapshot.verificationId); + } + }); + }); - const credential = await firebase.auth.PhoneAuthProvider.credential( - confirmResult.verificationId, - TEST_CODE_A, - ); + try { + const newSmsCode = await getLastSmsCode(newPhone); + const credential = await firebase.auth.PhoneAuthProvider.credential( + newPhoneVerificationId, + newSmsCode, + ); - //Update with number? - await firebase - .auth() - .currentUser.updatePhoneNumber(credential) - .then($ => $); + //Update with number? + await firebase + .auth() + .currentUser.updatePhoneNumber(credential) + .then($ => $); + } catch (e) { + throw e; + } - // TODO Add assertions, what exactly does this update. No phone number included to update? + firebase.auth().currentUser.phoneNumber.should.equal(newPhone); }); }); diff --git a/packages/firestore/e2e/helpers.js b/packages/firestore/e2e/helpers.js index a3c60a60ec..9a7d0a5289 100644 --- a/packages/firestore/e2e/helpers.js +++ b/packages/firestore/e2e/helpers.js @@ -1,4 +1,5 @@ /* eslint-disable no-console */ +const { getE2eTestProject, getE2eEmulatorHost } = require('../../app/e2e/helpers'); /* * Copyright (c) 2016-present Invertase Limited & Contributors @@ -18,28 +19,31 @@ */ const http = require('http'); -const deleteOptions = { - method: 'DELETE', - port: 8080, - host: '127.0.0.1', - path: '/emulator/v1/projects/react-native-firebase-testing/databases/(default)/documents', -}; - exports.wipe = async function wipe(debug = false) { - if (debug) { - console.time('wipe'); - } + const deleteOptions = { + method: 'DELETE', + port: 8080, + host: getE2eEmulatorHost(), + path: '/emulator/v1/projects/' + getE2eTestProject() + '/databases/(default)/documents', + }; - await new Promise((resolve, reject) => { - const req = http.request(deleteOptions); + try { + if (debug) { + console.time('wipe'); + } + await new Promise((resolve, reject) => { + const req = http.request(deleteOptions); - req.on('error', error => reject(error)); + req.on('error', error => reject(error)); - req.end(() => { - if (debug) { - console.timeEnd('wipe'); - } - resolve(); + req.end(() => { + if (debug) { + console.timeEnd('wipe'); + } + resolve(); + }); }); - }); + } catch (e) { + console.error('Unable to wipe firestore:', e); + } }; diff --git a/tests/app.js b/tests/app.js index 0ddfed57b9..6e10f5de52 100644 --- a/tests/app.js +++ b/tests/app.js @@ -44,6 +44,8 @@ jet.exposeContextProperty('module', firebase); const firestore = firebase.firestore(); firestore.settings({ host: 'localhost:8080', ssl: false, persistence: true }); +firebase.auth().useEmulator('http://localhost:9099'); + function Root() { return (