npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@autometa/page-components

v0.0.3

Published

**Autometa** Page Components Model is an (early and experimental) interpretation of the Page Object Model, built for use with selenium

Downloads

10

Readme

Page Components Model

Full Documentation/Tutorial Here

Autometa Page Components Model is an (early and experimental) interpretation of the Page Object Model, built for use with selenium

Quick Intro

Build Page Objects in a declarative manner, without instantiation or wiring logic, and no explicit waiting in tests:

export class MyHomePage extends WebPage {
  @component(By.className('login-btn'))
  login: Button;
}

And that's enough to start testing:

const url = process.env.MY_URL;
const builder = new Builder().forBrowser(Browser.CHROME);
const site = Site(url, builder);

describe('Clicking the login button', () => {
  let page: MyHomePage;
  beforeEach(async () => {
    page = await site.browse(MyHomePage);
  });

  it('should wait for the page to load and click the login button', () => {
    await page.waitForTitleIs('My Home Page!');
    await page.login.click();
  });
});

Easily create new components to better describe the intent of your Page Objects

export class MyLoginModal extends Component {
  @component(By.css('input[type="username"'))
  username: TextInput;
  @component(By.css('input[type="password"'))
  password: TextInput;
  // Automatically scoped to this modal. No conflict with
  // the '.login-btn' button in the parent DOM
  @component(By.className('login-btn'))
  login: Button;
}

Now start composing behavior

export class MyLoginModal extends Component {
  @component(By.css('input[type="username"'))
  username: TextInput;

  @component(By.css('input[type="password"'))
  password: TextInput;

  // Automatically scoped to this modal. No conflict with
  // the '.login-btn' button in the parent DOM.
  // Set wait-until condition and max timeout for slower items
  @component(By.className('login-btn'), Until.isDisplayed, 3500)
  login: Button;

  logUserIn = async (username: string, password: string) => {
    await this.username.write(username);
    await this.password.write(password);
    await login.click();
  };
}

And make a shortcut on our home-page if we like

export class MyLoginModal extends Component {
  @component(By.id('login-modal'))
  loginModal: MyLoginModal;

  @component(By.className('login-btn'))
  login: Button;

  logUserIn = async (username: string, password: string) => {
    await login.click();
    await this.loginModal.logUserIn(username, password);
  };
}

And begin using it in tests

const url = process.env.MY_URL;
const builder = new Builder().forBrowser(Browser.CHROME);
const site = Site(url, builder);

describe('Logging the user in', () => {
  let page: MyHomePage;
  beforeEach(async () => {
    page = await site.browse(MyHomePage);
  });

  it('should log the user in', () => {
    await page.waitForTitleIs('My Home Page!');
    await page.logUserIn('myname', 'mYpa55');
    await page.waitForTitleIs('mynames Profile')
  });
});

Other pages can also be bound for easy navigation

export class MyHomePage extends WebPage{
    @page()
    myProfilePage: MyProfilePage
}

Benefits

Declarative Model

Spend less time assembling Page Models. Declare models quickly and focus on writing tests.

Scoped Element Searches

Components automatically use their parents WebElement to initialize, and only use the WebDriver if the parent Page Object is a WebPage.

Lazy Initialization

Components initialize early so WebElements don't have to. WebElements are handled lazily, loaded only when acted upon, improving start up performance for element-based Page Object Models

Automatic Waiting

Lazily loaded elements when activated will automatically be waited for fluently. Until strategy can be configured multiple ways.

Automatic Staleness Handling

Elements will detect when they're stale and propagate a refresh request up the Page Model, forcing affected Components to lazily reload when next accessed instead of crashing.

Automatic Logging

(WIP) - only supports node console at this time.

Every interaction with the WebDriver is logged in detail with 'breadcrumbs' describing exactly what's happening in the tests

Example log:

  console.info
    Clicking On IndexPage[$root] >
      GridContainer[grid, By(css selector, .grid)] > 
        SimpleTypeDiv[simpleTypes, By(css selector, div:nth-of-type(1))] >
          Button[button, By(css selector, button)]( { text: "Click Me" } )

      at Button.#logActionOccurring (src/lib/meta-types/component.ts:558:17)

  console.info
    Searching for [ListItem, By(css selector, li)] in IndexPage[$root] > 
      GridContainer[grid, By(css selector, .grid)] >
        SimpleTypeDiv[simpleTypes, By(css selector, div:nth-of-type(1))] >
          UnorderedList[unorderedList, By(css selector, ul)]

      at ElementArray.<anonymous> (src/lib/meta-types/component.ts:540:17)

Easily Handle Collections of Similar Models

Patterns provided for handling dynamic or indeterminate content which share a model or structure.

Example html:

<ol>
  <li>first</li>
  <li>second</li>
  <li>third</li>
  <li>fourth</li>
</ol>

Example Page Model:

export class MyPageWithList extends WebPage {
  @collection(By.css('ol'), ListItem, By.css('li'))
  numericList: Collection<ListItem>;
}

Example test:

it('should check the list', () => {
  // array-like functions
  const expected = ['first', 'second', 'third', 'fourth'];

  await page.numericList.forEach(async (li, idx) => {
    expect(await li.text).toBe(expected[idx]);
  });

  const mapped = await page.numericList.map(async (li) => li.text);
  expect(mapped).toStrictEqual(expected);

  expect(await page.numericList.at(0)).toBe(expected[0]);

  // works with for...of
  for (const li of await page.numericList.values) {
    expect(await li.text).toBe(expected.shift());
  }
});