Creating a Modern Firebase Powered Application with TDD
Use reactive programming and tests to build a professional app
[Sprint Three] Logout
Adding a logout feature
PROModule Outline
- Source Code & Resources PRO
- Lesson 1: Introduction PUBLIC
- Lesson 2: The Structure of this Module PUBLIC
- Lesson 3: [Sprint One] Setting up Firebase PUBLIC
- Lesson 4: [Sprint One] Creating Security Rules with TDD PRO
- Lesson 5: [Sprint One] Testing Authentication PRO
- Lesson 6: [Sprint One] Component Store PRO
- Lesson 7: [Sprint One] Circumventing Firebase Authentication for E2E Tests PRO
- Lesson 8: [Sprint Two] Displaying Client List from Firestore PRO
- Lesson 9: [Sprint Two] - Adding Clients PRO
- Lesson 10: [Sprint Two] - Editing Clients PRO
- Lesson 11: [Sprint Two] - Client Details PRO
- Lesson 12: Preparing for Delivery PRO
- Lesson 13: Configuring for Production PRO
- Lesson 14: [Sprint Three] - Refactoring PRO
- Lesson 15: [Sprint Three] Setting up PWA PRO
- Lesson 16: [Sprint Three] Logout PRO
- Lesson 17: [Sprint Three] Delete a Client PRO
- Lesson 18: [Sprint Three] - Feedback Mechanism PRO
- Lesson 19: [Sprint Three] View Feedback PRO
- Lesson 20: More Styling PRO
- Lesson 21: [Sprint Four] - Refactoring Feedback PRO
- Lesson 22: [Sprint Four] - Feedback Dates PRO
- Lesson 23: [Sprint Four] - Client Survey PRO
- Lesson 24: [Sprint Four] - View Survey PRO
- Lesson 25: Final Touches PRO
- Lesson 26: Conclusion PRO
Lesson Outline
Feat: Ability to log out
Let's move on to our first proper user story for this sprint.
User story: As a massage therapist, I want the ability to log out, so that I can protect sensitive client information and interact with the application as a non-admin user
Project management
Remember to move the card for this task to the Test
column, and create a new task branch to work on.
For this one, we are just going to add a logout button on the client-dashboard
page. Let's start with an E2E test in clients.cy.ts
:
it('can log out', () => {
getLogoutButton().should('be.visible');
});
This test might seem a bit odd - it's not doing much. Typically we would want to actually click the button and see if it does what it is supposed to do - but this is a bit of a special case. Remember that we are using cypress-firebase
to bypass the authentication process, because we can't really get through that process properly just by interacting with the app with Cypress. If we click the logout button, we are going to mess with our E2E tests because we are going to be triggering real logout functionality. We avoid this problem with our login button because we completely bypass the authentication process if we are already authenticated (and we are thanks to cypress-firebase
). We don't have a mechanism for bypassing the logout functionality.
So, we are making a compromise here - a weak test to avoid the trouble of actually triggering a logout in our E2E tests. We are still going to test the logout functionality properly with our unit tests.
We have also added a new utility method here:
export const getLogoutButton = () => cy.get('[data-test="logout-button"]');
and now let's run it to make sure it fails:
Timed out retrying after 4000ms: Expected to find element: [data-test="logout-button"], but never found it.
If we're following the errors, we can now add a logout button to the template in client-dashboard.page.html
:
<ion-header>
<ion-toolbar>
<ion-title data-test="page-title">Clients</ion-title>
<ion-buttons slot="start">
<ion-button data-test="logout-button" (click)="authService.logout()">
<ion-icon slot="icon-only" name="power-outline"></ion-icon>
</ion-button>
</ion-buttons>
<ion-buttons slot="end">
<ion-button
data-test="add-client-button"
routerLink="add"
routerDirection="forward"
>
<ion-icon slot="icon-only" name="add"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
But of course we don't have a logout
method defined yet in our AuthService
so the test will still fail. Let's add a unit test for that now.
describe('AuthService', () => {
let service: AuthService;
let auth: AngularFireAuth.Auth;
let navCtrl: NavController;
beforeEach(() => {
jest.restoreAllMocks();
jest.clearAllMocks();
TestBed.configureTestingModule({
providers: [
AngularFireAuth.Auth,
{ provide: NavController, useValue: { navigateRoot: jest.fn() } },
],
});
service = TestBed.inject(AuthService);
auth = TestBed.inject(AngularFireAuth.Auth);
navCtrl = TestBed.inject(NavController);
});
describe('logout()', () => {
it('should call the signOut method', async () => {
jest.spyOn(AngularFireAuth, 'signOut');
await service.logout();
expect(AngularFireAuth.signOut).toHaveBeenCalledWith(auth);
});
it('should navigate back to the home page', async () => {
await service.logout();
expect(navCtrl.navigateRoot).toHaveBeenCalledWith('/home');
});
});
We have also added a test for navigating back to the root page when the logout is triggered. Before we can run our tests we will need to add a dummy logout
method:
Thanks for checking out the preview of this lesson!
You do not have the appropriate membership to view the full lesson. If you would like full access to this module you can view membership options (or log in if you are already have an appropriate membership).