Pragmatic Testing in AdonisJS with Japa #7.1

Testing Browser Form Submissions & Validation Feedback

In This Lesson

We'll learn how to test form submissions and validation using Japa's Browser Client and Playwright. We'll programmatically fill and submit our form and assert the visibility and contents of our validation errors.

Created by
@tomgobich
Published

Notes Used to Craft this Lesson

A common browser test you might perform is programmatically submitting a form and confirming that your user feedback and other behaviors are as you expect.

Let's use our login form as an example and test to confirm that our validation works and that old user inputs are applied after an unsuccessful submission.

test("see validation errors after unsuccessful submission", async ({
  visit,
  route,
}) => {});
Copied!

First, we'll visit our login page.

test("see validation errors after unsuccessful submission", async ({
  visit,
  route,
}) => {
  const page = await visit(route("auth.login.show"));
});
Copied!

Then, we can use Playwright's APIs to locate elements via selectors.

test("see validation errors after unsuccessful submission", async ({
  visit,
  route,
}) => {
  const page = await visit(route("auth.login.show"));

  await page.locator("input[type=email]");
});
Copied!

Once we have an element selected, we'll have a number of actionable methods at our disposal, like filling the input with a value.

test("see validation errors after unsuccessful submission", async ({
  visit,
  route,
}) => {
  const page = await visit(route("auth.login.show"));

  await page.locator("input[type=email]").fill("test@test.com");
});
Copied!

Great, now let's enter an invalid email address. The email field in the browser is of type email,l so we need something that will pass the browser's rule (so our form actually submits) but fails our validation rule, and "a@b.c" fits the bill just fine!

test("see validation errors after unsuccessful submission", async ({
  visit,
  route,
}) => {
  const page = await visit(route("auth.login.show"));

  await page.locator("input[type=email]").fill("a@b.c");
});
Copied!

Perfect, now this will render our login page and enter "a@b.c" into the email field. Let's enter a password next.

test("see validation errors after unsuccessful submission", async ({
  visit,
  route,
}) => {
  const page = await visit(route("auth.login.show"));

  await page.locator("input[type=email]").fill("a@b.c");
  await page.locator("input[type=password]").fill("something");
});
Copied!

Great, now we can go ahead and submit our form. Upon submission, it should hit our server and fail our validation, returning us back to the same page.

test("see validation errors after unsuccessful submission", async ({
  visit,
  route,
}) => {
  const page = await visit(route("auth.login.show"));

  await page.locator("input[type=email]").fill("a@b.c");
  await page.locator("input[type=password]").fill("something");

  // locate & click the submit button
  await page.locator("button[type=submit]").click();

  // wait for the page to load after redirecting back
  // this will specifically wait for the 'DOMContentLoaded' event to fire
  await page.waitForLoadState("domcontentloaded");

  // confirm we were redirected back to the login page
  await page.assertPath(route("auth.login.show"));
});
Copied!

Now that we've redirected back and confirmed we're on the right page, we can make some assertions! This should be shown the same as if we'd run through this flow ourselves in the browser, so we should be able to confirm our email validation error is shown.

test("see validation errors after unsuccessful submission", async ({
  visit,
  route,
}) => {
  const page = await visit(route("auth.login.show"));

  await page.locator("input[type=email]").fill("a@b.c");
  await page.locator("input[type=password]").fill("something");
  await page.locator("button[type=submit]").click();

  await page.waitForLoadState("domcontentloaded");
  await page.assertPath(route("auth.login.show"));

  // assert the id if our email validation error is visible on the page
  await page.assertVisible("#email-error");

  // assert that this id has the following innerText
  await page.assertText(
    "#email-error",
    "The email field must be a valid email address"
  );
});
Copied!

What would be an added plus is to also confirm the user's previously submitted values are still on the form, except for the password. For security purposes, that should always be cleared.

test("see validation errors after unsuccessful submission", async ({
  visit,
  route,
}) => {
  const page = await visit(route("auth.login.show"));

  await page.locator("input[type=email]").fill("a@b.c");
  await page.locator("input[type=password]").fill("something");
  await page.locator("button[type=submit]").click();

  await page.waitForLoadState("domcontentloaded");
  await page.assertPath(route("auth.login.show"));

  await page.assertVisible("#email-error");
  await page.assertText(
    "#email-error",
    "The email field must be a valid email address"
  );

  // assert that the email field's value is "a@b.c"
  await page.assertInputValue("input[type=email]", "a@b.c");

  // assert that our password was cleared
  await page.assertInputValue("input[type=password]", "");
});
Copied!

Finally, we can tack our remember me checkbox into this flow as well.

test("see validation errors after unsuccessful submission", async ({
  visit,
  route,
}) => {
  const page = await visit(route("auth.login.show"));

  await page.locator("input[type=email]").fill("a@b.c");
  await page.locator("input[type=password]").fill("something");

  // locate and check the remember me checkbox
  await page.locator("input[name=remember]").setChecked(true);

  await page.locator("button[type=submit]").click();

  await page.waitForLoadState("domcontentloaded");
  await page.assertPath(route("auth.login.show"));

  await page.assertVisible("#email-error");
  await page.assertText(
    "#email-error",
    "The email field must be a valid email address"
  );

  await page.assertInputValue("input[type=email]", "a@b.c");
  await page.assertInputValue("input[type=password]", "");

  // assert that it is still checked after invalid submission
  await page.assertChecked("input[name=remember]");
});
Copied!

Join the Discussion 0 comments

Create a free account to join in on the discussion
robot comment bubble

Be the first to comment!