Giter Site home page Giter Site logo

bahmutov / test-todomvc-using-app-actions Goto Github PK

View Code? Open in Web Editor NEW
105.0 1.0 24.0 2.2 MB

Example Cypress tests going from page objects to app actions

Home Page: https://www.cypress.io/blog/2019/01/03/stop-using-page-objects-and-start-using-app-actions/

HTML 3.01% JavaScript 33.93% TypeScript 63.07%
cypress cypress-io cypress-example

test-todomvc-using-app-actions's People

Contributors

bahmutov avatar renovate-bot avatar renovate[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

test-todomvc-using-app-actions's Issues

Fix the CircleCI build

Your configFile threw an error from: /root/project/cypress.config.ts

The error was thrown while executing your e2e.setupNodeEvents() function:

/root/project/node_modules/find-cypress-specs/src/index.js:204
    const e2eIgnorePattern = options.e2e?.specPattern || e2eDefaults.specPattern
                                         ^

SyntaxError: Unexpected token '.'

04 page object

I have rewritten all tests to use a pattern called Page Object. Instead of using cy.visit before each test highlight beforeEach the tests now call a method visit on todoPage object. This object is imported from another file highlight import line called todo.page.js

switch to file todo.page.js

Here is this file that defines the page object class and creates a page object instance. The class expand TodoPage class has methods to do basic operations our tests need, like visit the page expand visit, create default todos expand createTodos, etc. The page object is a single place to keep our test selectors and define complex actions on the page that our individual tests might need, expand toggle, expand todos method.

switch to spec.js

For example, look at the routing tests expand routing context, expand beforeEach. Then expand and concentrate on the individual test "should allow me to display active items" Our test only calls the page object methods, and those methods actually drive the application.

put .only on the test and show it running. Of course, the test still works the same way, the page object is just an extra layer on top of the application's DOM, it does not change the way the tests run really work, just the way the test code is organized.

switch to spec.js file and remove .only
Except that it does change the testing code. Now the things become both better and worse. For example, here is the test for persistence highlight test in "Persistence". Notice how the easy parts of the test are using the page object calls, but the parts original to this test are ... just regular Cypress calls. This is expected - Cypress command API is huge, and the number of commands and assertions that a realistic end-to-end test needs is also huge.

My tests now become a mix of page object calls and Cypress api calls, because I don't want to move everything into the page object - the page object would be growing and growing!

switch to todo.page.js file
An even worse problem is that I am writing this layer of code on top of the page UI. The page UI is brittle, it can change, but the page object is not going to know until the tests run. There are no tools to lint or statically check the HTML generated by your web application to warn you that the page object is now going to break. It is all built on sand.

And the effort going into writing and maintaining the page object code, especially as it becomes more complex, is NOT going towards improving the actual application. Because the page object is only for end-to-end testing - it's not running in production, it is really pure overhead.

But I have a solution for all these problems, and I call this solution "application actions". Here is what prompted me to move away from writing page objects in the first place. Look at the methods in the page object class. switch to todo.page.js and show methods. createTodo, toggle, clearCompleted - these methods names look very familiar. Where have we seen them before?

Oh, right, we have seen them in open todoModel.js our application's model class. Show and highlight addTodo, toggle, clearCompleted methods. We already have written code to do all these things that our page object has to do, but instead of going through the DOM we need to go into the application's internals directly from our tests. Can we do this?

Yes, thanks to Cypress architecture, and running inside the browser our testing code can reach into the app and call any method. In this application open app.jsx and shown window.Cypress when the application is running inside Cypress, there will be flag window.Cypress set, and we can expose the model reference.

switch to Cypress and open DevTools
Cypress iframes the app, and we can call JavaScript in that iframe by switching the DevTools context to "App" and then we can access the model reference. Let's add a new item execute model.addTodo('hey there'). The new item has been added. execute model.toggle(model.todos[0]) We just completed the first item!

05 app actions

I have rewritten my tests again and this time I have used app actions. My tests are using Cypress commands like cy.visit again (highlight cy.visit in beforeEach) without going through an intermediate object.

Here are my rules for app actions. If a group of tests like "New Todo" (expanda "New Todo" tests) are exercising a particular feature on the page, like the input element to add todos, then these tests are going through normal DOM selectors and actions, just like a real user would.

highlight const NEW_TODO = '.new-todo' selector.

expand test "should allow me to add todo items"
See how this test for adding new todo, goes through the page?

But other tests, that exercise other features, like Routing tests are different
scroll to and expand "Routing" tests

These tests are NOT interested in testing adding new todo items - this feature works, we know it, because our "New Todos" tests are passing. So the routing tests instead of using the DOM and typing new items again and again, can just invoke a method on the model instance and creat the todos to work with.

highlight beforeEach(addDefaultTodos)
In this beforeEach we are calling a utility function addDefaultTodos that I have defined in utils.js file.

switch to util.js file
_ highlight addDefaultTodos function_
See how this function gets the application's window context, grabs its property called model, and invokes method addTodo with 3 arguments? This instantly creates 3 todos, just like we did from DevTools console.

go back to spec.js file
set .only on test "should allow me to display active items"
Let's look at the first routing test here. We have created the initial 3 items using app action. Then we call toggle (highlight toggle(1)) which is also an app action - because the toggle through the page is actually tested by other tests. So routing should not retest the same feature.

But the clickFilter('Active') is a normal function that does go through the page and does click on the link in the DOM using Cypress commands. Because this is the page feature that this test is actually testing - it should not do the app action in this case.

save the spec file and switch to Cypress
rerun the test
The test created 3 items using an app action (highlight the INVOKE command), then toggled the middle one using an app action (highlight the toggled log message). Then it went through the DOM and clicked on the "Active" link and asserted that only the first and the third todo items are shown.

And notice that the test was fast (rerun the test), because it did not have to type 3 items, or click toggle on the page. App actions go directly into the application, bypassing the DOM. This makes tests run much faster.
switch to spec.js and remove .only and save the file
switch to Cypress
The tests now use mostly app actions to set the application's state before testing a particular feature, and this makes the tests run twice faster than the original page tests.

Besides speed and removing an entirely unnecessary level of abstraction compared to page objects, app actions have one other huge benefit.
switch to spec.js
Instead of building my test helpers on top of HTML and selectors that are not linted or statically checked, I am working directly against the application's code. This means that my JavaScript spec is calling into my app's JavaScript. And even in JavaScript in a modern code editor like VSCode, I can add this comment ts-check (highlight // @ts-check comment) and VSCode will check my test commands against what my app model can do!

For example, let me grab the window object, then its property model. What methods can I call on the model from my tests?
type

cy.window()
  .its('model')
  .then(model => {
    model.addTodo('first', 'second')
  })

and show intellisense after model.

IntelliSense is showing me the app actions I can call. And if I try to use an incorrect method, like window.foo (type window.foo or window.addTodos) TS check will warn me. It even will understand the types of the arguments, like window.addTodo(1, 2, 3) should be window.addTodo('first', 'second').

How does this work if both my app and my spec are plain JavaScript files. I have added reference path special comment to the spec file (highlight reference path=... comment). The TypeScript file model.d.ts is something I wrote. (click on model.d.ts top open it). It describes the interface for the model instance and adds property model to the global window object.

If I had written my TodoMVC app in TypeScript in the first place, I would not have to do this, but still, having an interface like this that ts-check can statically check against, gives me huge confidence in my tests. And the TodoModel class is a lot more stable and less likely to change than the page HTML.

end slides

slide Cypress = ...
In conclusion, in this presentation, I have shown an open source, free to use, modern end-to-end test runner Cypress that makes writing tests fun.

slide Page Objects
And when you have too much fun, you end up with lots of tests. Page Objects are meant to organize the test code to make it easy to maintain but lead to an extra layer of code on top of the application's page, which is unstable and hard to verify.

slide App Actions
App actions, on the other hand, are built on top of the application's internal API, like its model class. This makes them both stable in the long run, and easily statically type-checked.
click
Using app actions also keeps tests focused on a particular feature X, rather than constantly retesting unrelated page elements
click
And avoiding the DOM for setup makes the tests much much faster.

slide blog post
I have described what I have shown here in more details in a blog post that you can find on the Cypress blog.

_slide Thank You
Thank you for listening to me, you can find the slides and the source code at these links.
click
Also, go to the Cypress GitHub repo and give us a star, we always appreciate it
_click
I am Gleb Bahmutov, you can find me online, usually under my last name "bahmutov". Thanks everyone and happy testing.

Action Required: Fix Renovate Configuration

There is an error with this repository's Renovate configuration that needs to be fixed. As a precaution, Renovate will stop PRs until it is resolved.

Error type: Preset name not found within published preset config (monorepo:babel6). Note: this is a nested preset so please contact the preset author if you are unable to fix it yourself.

03 all UI tests

Let's test our application. Instead of typing each test I have pasted here the tests from the cypress-example-todomvc project. There are 30 tests that check if items can be added, edited, delete, completed and if the routing is working correctly.

highlight beforeEach cy.visit

Before each test we are going to visit the page, I have placed the actual base url in cypress.json settings file. Then each block of tests has its own context with name matching the feature of the app we are testing: adding new todos (highlight "New Todo"), editing a todo (highlight "Editing"), and routing for example (highlight "Routing").

switch to Cypress, rerun all tests

Here are these tests running. You can see the app really working, all actions happen through the DOM and through the DOM events. And all assertions and checks are using the DOM. Together 30 tests take about 30 seconds - Cypress never has to wait longer than necessary. As soon as command completes and the assertion passes, and Cypress is watching the DOM closely, the next command can start. This makes Cypress tests pretty quick.

Let's look at the block of tests that check the routing feature. expand "Routing" context. To show and test routing between active and completed todos, we need a few todos to exist. How do we create this initial list? Before each test we call cy.createDefaultTodos (highlight cy.createDefaultTodos) which is NOT a standard Cypress command. It is a custom command that I have written in commands.js file (switch to commands.js file).

expand createDefaultTodos definition

The custom command is just a series of Cypress commands, in this case, it types into the input box 3 times and clicks enter after each one. This should create 3 todo items in our application using the DOM.

switch back to spec.js
highlight single test "should allow me to display active items" and set it to .only but do not save

Let's look at this test in isolation. We have 3 items created before the test. Now we are going to toggle the middle item to mark it completed. highlight the commands

And then we will click on "Active" link highlight the commands and will confirm that only the first and third items are now shown in the new view. highlight TODO_ITEM_ONE and TODO_ITEM_THREE

save spec file
switch to Cypress

The test has finished, and we can see the individual steps in the time-traveling debugger. highlight steps in the command log We have 3 items initially, and we toggle the second one. Then we find the filters and click on the "Active" link. The web application routes to "active" view. And now there are two items displayed, the first todo and the third todo. The routing works.

switch back to spec file
So Cypress allows you to write a LOT of long tests really quickly, and each test goes through a typical user story. But how do you keep your tests organized? And how to make sure the tests are easy to maintain in the future? How do you avoid code duplication and brittle selectors? Let's rewrite our tests using Page Objects.

Issues with JS to TS conversion exercise

Hi, I am trying to use this repo as an exercise to work through the blog post you detailed in Convert Cypress Specs from JavaScript to TypeScript, but things are not quite going as detailed in the blog post.

I cloned 09-regression

Step 2
Already appears to have been done - the following is already at the start of adding-spec.js:

// type definitions for Cypress object "cy" and cypress-grep
/// <reference types="cypress-grep" />
// type definition for out TodoModel
/// <reference path='./model.d.ts' />
// @ts-check

And I already see the full info in the tooltip
So I tried deleting this, but still it does not seem to revert to just showing any:
image

Undeterred, I move on...
I complete steps 3 through 5 and all goes according to how it is in the blog post.

However, when I get to Step 6...

npm run lint

> [email protected] lint      
> tsc --noEmit --pretty --allowJs cypress/**/*.js

error TS6053: File 'cypress/**/*.js' not found.
  The file is in the program because:
    Root file specified for compilation        

Found 1 error.

Any idea what is going wrong?

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Awaiting Schedule

These updates are awaiting their schedule. Click on a checkbox to get an update now.

  • chore(deps): lock file maintenance

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

github-actions
.github/workflows/badges.yml
  • actions/checkout v4
  • stefanzweifel/git-auto-commit-action v4
  • ubuntu 22.04
.github/workflows/ci.yml
  • actions/checkout v4
  • ubuntu 22.04
.github/workflows/pr.yml
  • actions/checkout v4
  • ubuntu 22.04
.github/workflows/print-tests.yml
  • actions/checkout v4
  • ubuntu 22.04
.github/workflows/repeat-spec.yml
  • actions/checkout v4
  • ubuntu 22.04
.github/workflows/repeat-tag.yml
  • actions/checkout v4
  • ubuntu 22.04
.github/workflows/repeat-test.yml
  • actions/checkout v4
  • ubuntu 22.04
.github/workflows/spec-dependencies.yml
  • actions/checkout v4
  • stefanzweifel/git-auto-commit-action v4
  • ubuntu 22.04
.github/workflows/tagged-jobs.yml
  • actions/checkout v4
  • actions/checkout v4
  • actions/checkout v4
  • actions/checkout v4
  • actions/checkout v4
  • actions/checkout v4
  • actions/checkout v4
  • actions/checkout v4
  • ubuntu 22.04
  • ubuntu 22.04
  • ubuntu 22.04
  • ubuntu 22.04
  • ubuntu 22.04
  • ubuntu 22.04
  • ubuntu 22.04
  • ubuntu 22.04
.github/workflows/tagged.yml
  • actions/checkout v4
  • ubuntu 22.04
.github/workflows/trigger-circle-type.yml
  • ubuntu 22.04
.github/workflows/trigger-circleci.yml
  • ubuntu 22.04
npm
package.json
  • http-server 14.1.1
  • @bahmutov/cy-grep 1.9.17
  • cypress 13.8.1
  • cypress-repeat 2.3.4
  • cypress-watch-and-reload 1.10.14
  • find-cypress-specs 1.43.1
  • prettier 3.2.5
  • spec-change 1.10.0
  • start-server-and-test 1.15.5
  • typescript 4.9.5

  • Check this box to trigger a request for Renovate to run again on this repository

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.