Today’s Picture of the Day image

prev 2021-07-13Movidea Development Diary Cypress, contains selects DOM but type is undefined ts

cy.get("div[data-testid='2']").should((x) => {}); // x is JQuery<HTMLElement>
cy.contains("A B").should((x) => {}); // x is undefined

This is due to the implementation of both as a way to retrieve the DOM and as an assertion. It looks like this, but the cy is Chainable<undefined>, so Subject=undefined. Bad design, the names should be separated. cypress.d.ts

contains(content: string | number | RegExp, options?: Partial<Loggable & Timeoutable & CaseMatchable & Shadow>): Chainable<Subject>
contains<E extends Node = HTMLElement>(content: string | number | RegExp): Chainable<JQuery<E>>
...
interface cy extends Chainable<undefined> {}

Aside from that, I have data-testid attached to my implementation, so I thought I would make it easier by using it to make the selection a custom command.

support/index.ts

declare global {
  namespace Cypress {
    interface Chainable {
      ...
      testid(testid: string): Chainable<Element>;
      hasPosition(x: number, y: number): Chainable<Element>;
    }
  }
}
 
Cypress.Commands.add("testid", (testid: string) => {
  return cy.get(`*[data-testid='${testid}']`);
});
 
Cypress.Commands.add(
  "hasPosition",
  {
    prevSubject: true,
  },
  (subject: Cypress.Chainable<Element>, x: number, y: number) => {
    const cr = subject[0].getBoundingClientRect();
    expect(cr.x).equal(x);
    expect(cr.y).equal(y);
    return subject;
  }
);

with this cy.testid("1").hasPosition(159, 174); I can now write. However, this is not retried.

In fact, Custom Commands | Cypress Documentation describes how to create child commands, but the examples do not make assertions. The example does not make any assertions.

If you look at Assertions | Cypress Documentation, it says to add an assertion to Chai. Chai]. [Adding Chai Assertions https://github.com/cypress-io/cypress-example-recipes/tree/master/examples/extending-cypress__chai-assertions I wrote this in reference to []. support/index.ts

chai.use((_chai, utils) => {
  function hasPosition(options) {
    const [x, y] = options;
    const cr = this._obj[0].getBoundingClientRect();
 
    this.assert(
      cr.x === x,
      `expected x:${cr.x} is ${x}`,
      `expected x:${cr.x} is not ${x}`,
      cr.x
    );
    this.assert(
      cr.y === y,
      `expected y:${cr.y} is ${y}`,
      `expected y:${cr.y} is not ${y}`,
      cr.y
    );
  }
 
  _chai.Assertion.addMethod("hasPosition", hasPosition);
});

with this cy.testid("1").should("hasPosition", [159, 174]); I can now write. If it were a should, I’d say it’s a have, but that’s a Chai thing to say in the first place. cy.testid("1").has.position([159, 174]); I started to feel as if I should be able to write “I’m sorry, I’m sorry, I’m sorry, I’m sorry,” but this was around the border between Cypress and Chai, and the conversation was complicated, so I held off.

The following test can now be written concisely test.ts

// before
cy.get("div[data-testid='1']").should((x) => {
  expect(x[0].getBoundingClientRect().x).equal(x1);
  expect(x[0].getBoundingClientRect().y).equal(y1);
});
// after
cy.testid("1").should("hasPosition", [x1, y1]);

I’m getting used to custom commands a lot, and I realized that I should make updateGlobal a custom command since I use it so often in the first place. support/index.ts

Cypress.Commands.add("updateGlobal", (callback: (g: State) => void) => {
  return cy.movidea((movidea) => {
    movidea.updateGlobal(callback);
  });
});

It is now possible to update the status without having to go through movidea every time, as shown below. test.ts

// before
cy.movidea((movidea) => {
  movidea.updateGlobal((g) => {
    g.itemStore["1"].position = [dx, 0];
  });
});
// after
cy.updateGlobal((g) => {
  g.itemStore["1"].position = [dx, 0];
});

The test is “If item #3 (sticky B) is moved, the position of item #1 (group) should be where and where and where. (I should have used an easy-to-understand name for the item ID, too.) test.ts

cy.updateGlobal((g) => {
  g.itemStore["3"].position = [-100, 0];
});
cy.testid("1").should("hasPosition", [55, 170]);

image

And when the group is closed from this state, the location shifts. Let’s fix this next. test.ts

cy.updateGlobal((g) => {
  (g.itemStore["1"] as GroupItem).isOpen = false;
});

image

Cypress, I could see snapshots of each stage of the test so I could check for “bugs that are slightly off by 4 pixels”, useful! image

Let’s display the JSON exported from Regroup again at this point. Now that we’ve got it right, it’s easy to see what’s causing the bug: the bounding box for the group within a group is losing the information about the parallelism. image

Fixed. It spills over into a lot of different areas, but it’s good to have a test case so it’s easy to check how it works. It now looks decent. image Visible in Regroup from 60df19d4aff09e0000c6d717. image Almost the same! All test cases so far are also successful.

Tomorrow I’ll work around menus and dialog. https://material-ui.com/components/menus/ I reread OOUI. I was re-reading the OOUI description, and both “New” and “Import” are task oriented. What if we were to make it an object oriented UI? I wonder if the import wizard should be open from the beginning, since it’s obvious that it’s a “new creation” when you access a non-existing map URL, and it’s unlikely that you don’t put in content after creating a new map.

I think it’s “add sticky”, not “import”.

  • What is the UI for importing from a URL?

Next 2021-07-15Movidea Development Diary


This page is auto-translated from /nishio/2021-07-14Movidea開発日記 using DeepL. If you looks something interesting but the auto-translated English is not good enough to understand it, feel free to let me know at @nishio_en. I’m very happy to spread my thought to non-Japanese readers.