craigwh10 / dom-locator-generator

Generate locator objects for all of your frontend tests from the code you write.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

badge dlg-ci/build

Quick start

$ yarn
$ yarn test:all

Vision of usage

Method

/**
 * file: relative root or component to infer file path
 * attributesToGet: data-testid, placeholder, class, className, testId, ... (element/component attribute)
 */
type getLocator = (file: ReactElement | string, attributesToGet: string[], options: Options) => {
    // how best to identify has not been decided yet.
    [attributeIdentifier]: {
        xPath: {
            // dependent on spikes around this.
            // there are packages to parse to xPath
            // [data-testid='val']
            default: string     
        },
       // val
       default: string;
    }
}

interface Options {
   /**
    * component: searches only within the scope of the file.
    * page: searches deep nested dependencies within the file.
    */
   scope: 'component' | 'page'
}

Comparison

with @dlg without @dlg
Unit
// Input.test.tsx

import { Input } from "../../Input";
import { getLocators } from "@dlg/react";
import { render, fireEvent, container } from "@testing-library/react";

const elements = getLocators(Input, ["data-testid"], { scope: "component" });

describe(Input.name, () => {
   it("should call aFunction if type in input and click submit", () => {
      const spy = jest.spyOn(window, "alert");
      const element = render();
      
      const input = container.getByTestId(elements["input-0"]);
      const button = container.getByTestId(elements["button-1"]);
      
      fireEvent.type(input, "hello");
      fireEvent.click(button);
      
      expect(input).toHaveTextContent("");
      expect(spy).toBeCalledWith("hello");
   });
   it("should not call aFunction if no input and click submit", () => {
      const spy = jest.spyOn(window, "alert");
      const element = render();
      
      const button = container.getByTestId(elements["button1"]);
      
      fireEvent.click(button);
      
      expect(input).toHaveTextContent("");
      expect(spy).not.toBeCalledWith("hello"); 
   });
});
// Input.test.tsx

import { Input } from "../../Input";
import { render, fireEvent, container } from "@testing-library/react";

describe(Input.name, () => {
   it("should call aFunction if type in input and click submit", () => {
      const spy = jest.spyOn(window, "alert");
      const element = render();
         
      const input = container.getByTestId("input");
      const button = container.getByTestId("button1");
      
      fireEvent.type(input, "hello");
      fireEvent.click(button);
      
      expect(input).toHaveTextContent("");
      expect(spy).toBeCalledWith("hello");
   });
   it("should not call aFunction if no input and click submit", () => {
      const spy = jest.spyOn(window, "alert");
      const element = render();
      
      const button = container.getByTestId("button1");
      
      fireEvent.click(button);
      
      expect(input).toHaveTextContent("");
      expect(spy).not.toBeCalledWith("hello");
   });
});
Regression
// PageObjects/Home.ts

import { getLocators } from "@dlg/react";

const elements = getLocators("../../../src/pages", ["data-testid"], {
   scope: "page",
});

export class HomePage {
   get Input(): Promise {
      return $(elements["input-0"].xPath);
   }
   get Button(): Promise {
      return $(elements["button-1"].xPath);
   }

   get ItemListOptions(): Promise {
      // could be $$(elements['list'].xPath.startWith())
      return $$(elements["list"].xPath.modify((item) => item.replace("=", "\*=")));
   }
}
// PageObjects/Home.ts

export class HomePage {
   get Input(): Promise {
      return $('[data-testid="input"]');
   }
   get Button(): Promise {
      return $('[data-testid="button1"]');
   }
   get ItemListOptions(): Promise {
      return $$('[data-testid*="list"])');
   }
}
  • 1 source of truth
    • Less replication of work
    • Automatically maintained
  • Type checking on missing keys
  • Not reactive (only when test fails)
    • Annoying to maintain
    • No type safety
  • Multiple sources of truth
  • Less code
  • Easy to change individually

Milestones

  • Generate testId locator file for React. (MVP)
    • Doing react as this is the framework I'm more familiar with.
  • Generate testId locator file for Vue/Svelte/Angular
    • Wouldn't mind help

Contributing

  • Kanban Board
  • Ensure to read through relevant readmes
  • Comment on tickets if you require support
  • Raise issues on board for enhancements/bugs/requests.

Onboarding

About

Generate locator objects for all of your frontend tests from the code you write.


Languages

Language:TypeScript 92.4%Language:JavaScript 7.1%Language:Shell 0.4%