trmnl_preview 0.18.1 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +2 -2
- data/lib/trmnlp/testing/publishable_recipe.rb +40 -1
- data/lib/trmnlp/testing/rspec.rb +15 -0
- data/lib/trmnlp/version.rb +1 -1
- data/templates/init/tests/plugin_spec.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 10d19ade9dca541649486f6b8d105277f15400451bbde285591815f4cdc8f9a1
|
|
4
|
+
data.tar.gz: d83fde4dc7312ec8316027b5f9f6370bca2194228e5ad55dfc92874a260c87d4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 25d1251bbb5421d2fe762d46f20ce675d59179e69e42ab44dd6b25ee05dc5bf7609aca22a71bafe77841492b10797aa93153252917078cee61a13b31a2790c59
|
|
7
|
+
data.tar.gz: 5e1dd277a7aa823e866ca8f2c674751baa04b0e4bcc00b7522064064887eb4c6f246e15f1b3884d8a64fc605005a67c7bc8274364bba7d29bf6fc7739d60def7
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
|
|
2
2
|
# Changelog
|
|
3
3
|
|
|
4
|
+
## 0.19.0
|
|
5
|
+
|
|
6
|
+
- `it_behaves_like 'a publishable recipe'` also checks every screen for leaked values (`undefined`, `NaN`, `null`, `[object Object]`, `Liquid error`, raw `{{` or `{%`), draws the full view when the API answers with nothing, answers 500 or cannot be reached, and draws it with each option of every select field. New matchers: `have_no_leaked_text` and `have_no_transform_error`. (#181)
|
|
7
|
+
|
|
4
8
|
## 0.18.1
|
|
5
9
|
|
|
6
10
|
- Pages narrower than a Firefox window render, such as a 480x800 TRMNL OG in portrait, in `serve`, `build` and `test`. trmnlp sets the page's viewport with WebDriver BiDi, as TRMNL does, instead of resizing the window, which Firefox will not size under about 500px. (#178)
|
data/README.md
CHANGED
|
@@ -505,8 +505,8 @@ end
|
|
|
505
505
|
- `mocks:` answer every request the run makes: polling urls, and the transform's own requests in any language, HTTPS included. Keys are urls, with `*` wildcards, a Regexp, or a method first (`'POST https://...'`). Values are `{ json:, body:, status:, headers:, delay:, body_delay:, advance_clock:, error: :reset }` (times in seconds: `body_delay:` sends the headers first and the body later, `advance_clock:` moves the transform's clock on when it answers), a string body, a lambda that takes the request, or an array of answers used in order. An unmocked request gets a 599. `requests` lists every request with its `status`, and `aborted: true` for one the transform gave up on before its answer arrived; `duration_ms` is how long the transform ran.
|
|
506
506
|
- `now:` starts every clock: Liquid's, the markup's scripts', and the transform's, through libfaketime (`brew install libfaketime` or `apt-get install libfaketime`; already in the Docker image). On macOS the interpreter must come from brew, mise or similar, since macOS will not hand libfaketime to its own `/usr/bin` binaries.
|
|
507
507
|
- `trmnlp test --report report` also writes `report/index.html` and `report/report.json`: every example with each screen it rendered (with a switch that outlines every drawn box, and the page's problems) and each transform it ran (time, memory, requests). Under GitHub Actions the counts and failures go to the run's summary too.
|
|
508
|
-
- `it_behaves_like 'a publishable recipe'` is what a recipe should hold before it is published: every view draws without overflow or page errors on the TRMNL OG (1-bit, and 2-bit in landscape and portrait) and the TRMNL X (landscape and portrait), and the transform runs without error within TRMNL's limits. It uses the group's `mocks` and `custom_fields` when the group defines them; `screens: [{ device: 'kobo_libra_2' }, ...]` draws on other devices.
|
|
509
|
-
- `match_snapshot` stores a missing snapshot under `tests/snapshots/<os>/` and fails one on CI; `trmnlp test --update` rewrites them. Fonts render differently per operating system, so run tests in the Docker image when CI should share your snapshots. `fit_image_size_limit` checks the PNG against the model's limit.
|
|
508
|
+
- `it_behaves_like 'a publishable recipe'` is what a recipe should hold before it is published: every view draws without overflow or page errors on the TRMNL OG (1-bit, and 2-bit in landscape and portrait) and the TRMNL X (landscape and portrait), and the transform runs without error within TRMNL's limits. Every screen is also checked for leaked values (`undefined`, `NaN`, `null`, `[object Object]`, `Liquid error`, raw `{{`); the full view must still draw when the API answers with nothing, answers 500 or cannot be reached; and it is drawn with each option of every select field (the first and last when a field has more than 20). It uses the group's `mocks` and `custom_fields` when the group defines them; `screens: [{ device: 'kobo_libra_2' }, ...]` draws on other devices.
|
|
509
|
+
- `match_snapshot` stores a missing snapshot under `tests/snapshots/<os>/` and fails one on CI; `trmnlp test --update` rewrites them. Fonts render differently per operating system, so run tests in the Docker image when CI should share your snapshots. `fit_image_size_limit` checks the PNG against the model's limit, `have_no_leaked_text` the drawn text for leaked values, and `have_no_transform_error` a render's transform.
|
|
510
510
|
|
|
511
511
|
## Development
|
|
512
512
|
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'yaml'
|
|
4
|
+
|
|
5
|
+
require_relative '../select_field_choices'
|
|
6
|
+
|
|
3
7
|
module TRMNLP
|
|
4
8
|
module Testing
|
|
5
9
|
# The screens a recipe is drawn on: TRMNL's own devices, in landscape and portrait.
|
|
@@ -8,20 +12,55 @@ module TRMNLP
|
|
|
8
12
|
{ device: 'v2' }, { device: 'v2', orientation: :portrait }
|
|
9
13
|
].freeze
|
|
10
14
|
PUBLISHABLE_RECIPE_VIEWS = %w[full half_horizontal half_vertical quadrant].freeze
|
|
15
|
+
PUBLISHABLE_RECIPE_API_FAILURES = {
|
|
16
|
+
'answers with nothing' => { json: {} }, 'answers 500' => { status: 500, body: '' },
|
|
17
|
+
'cannot be reached' => { error: :reset }
|
|
18
|
+
}.freeze
|
|
19
|
+
# A field with more options than this is drawn with its first and last.
|
|
20
|
+
PUBLISHABLE_RECIPE_MAX_OPTIONS = 20
|
|
21
|
+
|
|
22
|
+
# Each select field's keyname and the values its options save, from the plugin's settings.yml.
|
|
23
|
+
def self.select_field_values
|
|
24
|
+
path = File.join(plugin_dir, 'src', 'settings.yml')
|
|
25
|
+
fields = File.exist?(path) ? Array(YAML.safe_load_file(path, aliases: true)&.dig('custom_fields')) : []
|
|
26
|
+
fields.select { it['field_type'] == 'select' }.to_h do |field|
|
|
27
|
+
values = SelectFieldChoices.new(field).pairs.map { it.last.to_s }
|
|
28
|
+
[field['keyname'], values.size > PUBLISHABLE_RECIPE_MAX_OPTIONS ? values.values_at(0, -1) : values]
|
|
29
|
+
end
|
|
30
|
+
end
|
|
11
31
|
end
|
|
12
32
|
end
|
|
13
33
|
|
|
34
|
+
# rubocop:disable-next Metrics/BlockLength -- one shared group, one check per example
|
|
14
35
|
RSpec.shared_examples 'a publishable recipe' do |screens: TRMNLP::Testing::PUBLISHABLE_RECIPE_SCREENS|
|
|
15
36
|
let(:recipe_inputs) do
|
|
16
37
|
{ mocks: respond_to?(:mocks) ? mocks : {}, custom_fields: respond_to?(:custom_fields) ? custom_fields : {} }
|
|
17
38
|
end
|
|
18
39
|
|
|
40
|
+
def draw_cleanly = have_no_overflow.and(have_no_problems).and(have_no_leaked_text)
|
|
41
|
+
|
|
19
42
|
TRMNLP::Testing::PUBLISHABLE_RECIPE_VIEWS.product(screens).each do |view, screen|
|
|
20
43
|
device = screen[:device].is_a?(Hash) ? screen[:device][:model] : screen[:device]
|
|
21
44
|
screen_name = [device, screen[:orientation]].compact.join(' ')
|
|
22
45
|
|
|
23
46
|
it "draws the #{view} view on #{screen_name} without overflow or page errors" do
|
|
24
|
-
expect(trmnl.render(view:, **screen, **recipe_inputs)).to
|
|
47
|
+
expect(trmnl.render(view:, **screen, **recipe_inputs)).to draw_cleanly
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
TRMNLP::Testing::PUBLISHABLE_RECIPE_API_FAILURES.each do |failure, answer|
|
|
52
|
+
it "draws the full view when the API #{failure}" do
|
|
53
|
+
screen = trmnl.render(**screens.first, **recipe_inputs, mocks: { '*' => answer })
|
|
54
|
+
expect(screen).to have_no_problems.and(have_no_leaked_text).and(have_no_transform_error)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
TRMNLP::Testing.select_field_values.each do |keyname, values|
|
|
59
|
+
values.each do |value|
|
|
60
|
+
it "draws the full view with #{keyname} set to #{value}" do
|
|
61
|
+
custom_fields = recipe_inputs[:custom_fields].merge(keyname => value)
|
|
62
|
+
expect(trmnl.render(**screens.first, **recipe_inputs, custom_fields:)).to draw_cleanly
|
|
63
|
+
end
|
|
25
64
|
end
|
|
26
65
|
end
|
|
27
66
|
|
data/lib/trmnlp/testing/rspec.rb
CHANGED
|
@@ -18,6 +18,8 @@ require_relative 'snapshot'
|
|
|
18
18
|
module TRMNLP
|
|
19
19
|
module Testing
|
|
20
20
|
PLUGIN_DIR_ENV_KEY = 'TRMNLP_PLUGIN_DIR'
|
|
21
|
+
# What a template or script draws when its data is missing.
|
|
22
|
+
LEAKED_TEXT_PATTERN = /Liquid (?:syntax )?error|\bundefined\b|\bNaN\b|\bnull\b|\[object Object\]|\{\{|\{%/
|
|
21
23
|
|
|
22
24
|
def self.plugin_dir = ENV.fetch(PLUGIN_DIR_ENV_KEY, Dir.pwd)
|
|
23
25
|
|
|
@@ -53,6 +55,19 @@ RSpec::Matchers.define :stay_within_serverless_limits do
|
|
|
53
55
|
end
|
|
54
56
|
end
|
|
55
57
|
|
|
58
|
+
RSpec::Matchers.define :have_no_leaked_text do
|
|
59
|
+
match { |screen| screen.text.scan(TRMNLP::Testing::LEAKED_TEXT_PATTERN).empty? }
|
|
60
|
+
failure_message do |screen|
|
|
61
|
+
leaked = screen.text.scan(TRMNLP::Testing::LEAKED_TEXT_PATTERN).uniq
|
|
62
|
+
"expected no leaked values on #{screen.inspect}, but it shows: #{leaked.join(', ')}"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
RSpec::Matchers.define :have_no_transform_error do
|
|
67
|
+
match { |screen| screen.result.error.nil? }
|
|
68
|
+
failure_message { |screen| "expected the transform to run without error, but: #{screen.result.error}" }
|
|
69
|
+
end
|
|
70
|
+
|
|
56
71
|
RSpec::Matchers.define :have_no_problems do
|
|
57
72
|
match { |screen| screen.problems.empty? }
|
|
58
73
|
failure_message { |screen| "expected no problems on #{screen.inspect}, but: #{screen.problems.join('; ')}" }
|
data/lib/trmnlp/version.rb
CHANGED
|
@@ -10,7 +10,7 @@ RSpec.describe 'My Plugin' do
|
|
|
10
10
|
# The values of the custom fields in src/settings.yml to test with.
|
|
11
11
|
let(:custom_fields) { {} }
|
|
12
12
|
|
|
13
|
-
# Every view on TRMNL's devices
|
|
13
|
+
# Every view on TRMNL's devices, a failing API and every select option, checked for overflow and errors.
|
|
14
14
|
it_behaves_like 'a publishable recipe'
|
|
15
15
|
|
|
16
16
|
# A picture of the full view, compared pixel for pixel on every run. Record it once with
|