How to set token in IndexedDB.ngStorage.localStorage (@ngx-pwa/local-storage plugin) to automatically authenticate my Angular app for Cypress test

382 views Asked by At

I'm actually able to test the login of my app in Cypress by going through all the form, entering the username, password and clicking on the login button.

This set the token in my browser in the (Application) IndexedDB - ngStorage - localStorage section. Because I'm using the @ngx-pwa/local-storage plugin.

Then Cypress is able to go on the home page of my App.

But how to set the token in the browser, without using the UI?

I try to use localForage plugin but it set the token in the localForage section of my browser. And my app is not able to read my token.

I try to use window.localStorage.setItem('token', myToken); but it set the token in the localStorage of my browser.

How can I set my token in the good location: (Application) IndexedDB - ngStorage - localStorage

1

There are 1 answers

0
Nicolas On

I've not found the solution, though the plugin @ngx-pwa/local-storage is used by many people programming in Angular.

I don't know if there is a better and cleaner solution than this one.

I used window.

cypress/support/window-helper.ts


export class WindowHelper {
  static addLocalStorage(
    window: Cypress.AUTWindow,
    key: string, 
    value: any
  ): Promise<IDBRequest<IDBValidKey>> {
    return WindowHelper.openNgStorage(window)
    .then(
      (store: IDBObjectStore) => store.add(value, key)
    );
  }
  
  static async getLocalStorage(
    window: Cypress.AUTWindow
  ): Promise<Map<string, JSONValue>> {

    const openCursor: IDBRequest<IDBCursorWithValue> = await WindowHelper
    .openNgStorage(window)
    .then(
      (store: IDBObjectStore) => store.openCursor()
    );

    let objects: Map<string, JSONValue> = new Map<string, JSONValue>();

    return new Promise((resolve, reject) => {
      openCursor.onsuccess = (event) => {
        const cursor: IDBCursorWithValue = event.target['result'];

        if (cursor) {
          objects.set(cursor.key.toString(), cursor.value);
          cursor.continue();
        }
        else {
          resolve(objects)
        }
      };
      openCursor.onerror = () => {
        const errorMessage = {
          title: 'Failed to open cursor',
          this: openCursor,
          function: 'getLocalStorage',
          result: openCursor.result
        }
        console.error(errorMessage);
        reject(errorMessage);
      }
    })
  }

  private static openNgStorage(window: Cypress.AUTWindow): Promise<IDBObjectStore> {
    const open: IDBOpenDBRequest = window.indexedDB.open("ngStorage");

    return new Promise(
      (resolve, reject) => {
        open.onsuccess = () => {
          resolve(
            open.result
            .transaction("localStorage", "readwrite")
            .objectStore("localStorage")
          );
        }
        open.onerror = () => {
          const errorMessage = {
            title: 'Failed to open localStorage',
            this: open,
            function: 'addLocalStorage',
            result: open.result
          }
          console.error(errorMessage);
          reject(errorMessage);
        }
      }
    );
  }
}

cypress/support/commands.ts

import { WindowHelper } from './window-helper'

Cypress.Commands.add('getAndSetToken', () => {
  Cypress.log({
    message: 'Requests token and sets in local storage (IndexedDB.ngStorage.localStorage)',
    displayName: 'GetToken'
  });

  let result: Token;

  return cy.signin().then((response: Cypress.Response<JSONObject>) => {
    const {
      token
    } = response.body;
    result = token as string;

    return cy.addLocalStorage('token', token)
  })
  .then(() => result);
});

Cypress.Commands.add('addLocalStorage', (key: string, value: JSONValue) => {
  return cy.window()
  .then(
    (window: Cypress.AUTWindow) => 
      WindowHelper.addLocalStorage(window, key, value)
  )
});

Cypress.Commands.add('getLocalStorage', () => {
  return cy.window()
  .then(
    (window: Cypress.AUTWindow) =>
      WindowHelper.getLocalStorage(window)
      .then((map) => Object.fromEntries(map))
  )
});

cypress/support/index.ts

declare global {
  namespace Cypress {
    interface Chainable {

      /**
     * Custom command to get and set token to browser localStorage
     * requested from back.
     * @return string token 
     * @example cy.getAndSetToken()
     */
      getAndSetToken(): Chainable<Token>

      /**
     * Custom command to get and set token to browser localStorage (IndexedDB.ngStorage.localStorage)
     * requested from back.
     * @example cy.addLocalStorage('token', 'my-token-value')
     */
      addLocalStorage(key: string, value: any): Chainable<IDBRequest<IDBValidKey>>

      /**
     * Custom command to get and set token to browser localStorage (IndexedDB.ngStorage.localStorage)
     * requested from back.
     * @example cy.getLocalStorage()
     */
       getLocalStorage(): Chainable<JSONObject>
    }
  } 
}

use in spec file

  describe('signin without UI', () => {
    beforeEach(() => {
      cy.getAndSetToken()
    })
  
    it('logs in programmatically without using the UI', () => {
      cy.visit('/')
    })
  })

This subject help me: link

Thanks to rajd90