Search code examples
javascriptplaywrightpageobjects

JavaScript class vs function returning object


So im pretty much used to always using classes when writing automation code (Using the Page object Model). However i've came across some PoM/Component sample code that im not really used to seeing. For example this header.ts file is not what I am used to seeing:

import { Page, expect } from "@playwright/test";

export function Header(page: Page) {
  const self = page.locator('header');

  const input = self.getByPlaceholder('What needs to be done?');
  const toggleAllButton = page.getByLabel('Mark all as complete');

  const completeAll = () => toggleAllButton.check();
  const uncompleteAll = () => toggleAllButton.uncheck();
   
  const addTodo = async (text: string) => {
    await input.fill(text);
    await input.press('Enter');
  }; 

  return {
    addTodo,
    completeAll,
    uncompleteAll,
    expect: () => ({
      ...expect(self),
      toAllowUncompleteAll: (allow = true) => expect(toggleAllButton).toBeChecked({ checked: allow }),
      toHaveEmptyInput: () => expect(input).toBeEmpty()
    })
  };
} 

Now this is using Playwright specifically, but the object/component model is similar. In classes you don't typically "return" anything, but here they are returning all the functions to be used elsewhere.

I guess my main question is:

  1. Why do we have to return the functions within here as opposed to a class once you create the instance object we don't have to return anything.
  2. Is there really any advantage to doing it this way vs a class? (Feels messier?)
  3. I don't under the ...expect(self) and what that is doing. To be honest why is self used here anyways? (Im not familiar with how self works in functions)

Solution

  • It's the Revealing Module Design Pattern.

    By using this you can simply define all functions and variables in the private scope and return an anonymous object with pointers to the private functionality to expose to other code areas where required utilizing closures.

    This is a very clean way of specifying the public functions that you want to expose to the outside world. I think , the biggest advantage of revealing module pattern is that provides cleaner code structure.

    The revealing module pattern is used to provide an abstraction over private implementations by providing public APIs. I think more or less we achieve the same thing using classes.

    Simple Code Example:

    // JavaScript Revealing Module Pattern
    const Person = (function () {
      // Private Properties
      let name;
      let lastName;
    
      function publicSetName(_name, _lastName) {
        name = _name;
        lastName = _lastName;
      }
    
      function publicGetName() { return `${name} ${lastName}` }
    
      /**
       * Reveal public pointers to 
       * private functions and properties
       */
      return {
        setName: publicSetName,
        getName: publicGetName
      }
    })();
    
    const client = Person;
    
    client.setName('Vishu', 'A.');
    console.log(client.getName()); // Vishu A.
    // The name property is not accessible.
    console.log(client.name); // undefined
    

    This was used to emulate the concept of classes earlier but including public/private properties and methods, protecting certain parts from the global scope by the use of closures.

    In my personal opinion , I find it more cleaner solution than classes.

    For further Reading: https://javascript.plainenglish.io/module-design-pattern-vs-classes-in-javascript-45568e74ad2a