/**
* 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'
}
|
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
|
- Kanban Board
- Ensure to read through relevant readmes
- Comment on tickets if you require support
- Raise issues on board for enhancements/bugs/requests.
- dependencies:
- testing:
- core stuff:
- selectors (I'm generally aware of):
- /notes/plan.md
- regex:
- https://regex101.com
- ECMAScript flavour (note JS does not support features such as negative/pos lookahead/behind groups)