diff --git a/_posts/2023-09-01-pixel-mocking.md b/_posts/2023-09-01-pixel-mocking.md new file mode 100644 index 00000000..8e76ec10 --- /dev/null +++ b/_posts/2023-09-01-pixel-mocking.md @@ -0,0 +1,161 @@ +--- +title: Cockpit 299 +author: mvo +date: '2023-09-01' +tags: cockpit +slug: pixel-mocking +category: tutorial +summary: 'Mocking the DOM for Better Pixels, plus statistics' +--- + +When testing pixels, once needs to deal with parts of the UI that +change from one run of the test to the next. Things like MAC +addresses, time stamps, and UUIDs are obvious examples. + +The pixel test machinery did have mechanisms for this since the +beginning, and now we have added one more. + +## Ignoring pixels + +My initial idea was to use the alpha channel in reference images to +mark specific pixels where the reference doesn't need to match the +current pixels. I did indeed think that we would open GIMP and +carefully paint in the areas that we expect to be unstable. That never +happened, obviously. The code to support this is still there, but it +was clear very early on, that this is too much work and also a quite +non-obvious feature that people would be struggling with to no end. + +So we had the `ignore` argument for `assert_pixels`. With this, you +could specify right in the tests which DOM elements were allowed to +differ from one run to the next. This was much easier to maintain and +made it much more obvious what was going on. + +However, different element content might not only change the pixels +inside the element itself, it might also change the size of the +element, and that in turn might change the layout of other +elements. + +For example, if a DOM element that should be ignored gets smaller from +one run to the next, the old pixels in the reference image that are +now outside of this element (but used to be inside) will now count as +a difference. + +In reality, this is much less of a problem than it might seem: We +usually compare big containers for pixel tests, such as a whole dialog +or top-level panels. These larger elements are not supposed to chanmge +size when thei content changes. + +However, their internal layout might still be affected: The most +stubborn example is maybe table layout. If the size of the content of +a cell changed at all, the widths of all table columns might +change. We ended up ignoring most content of tables, which is a bit +unsatisfactory. + +## Fake pixels + +So we started exploring another approach: Replacing the real data in +the UI with "mock data". This is conceptually simple, but I got a +serious case of "perfection is the enemy of the good". I was imagining +all kinds of theoretical problems, and I wanted to avoid them all with +a bullet proof implementation. This failed, but the journey might be +worth telling. + +The main issue is that the DOM is "owned" by React, and modifiying it +without involcing React seemed like asking for trouble. React might +decide to render just when the tests had inserted their mock +data. Also, as it turns out later, React keeps references to the nodes +it has created and will crash if it finds the DOM in an unexpected +state. + +So, the first try was to involve the Cockpit UI code itself in the +mocking process. People would write something like this to implement a +mockable React component: + +``` +const Thing = ({ id }) => { + const mock = useMock('thing'); + + return