ts_schema_spec 0.6.0 → 0.6.1
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/README.md +13 -10
- data/lib/ts_schema_spec/rspec.rb +11 -2
- data/lib/ts_schema_spec/version.rb +1 -1
- data/skills/ts-schema-spec/SKILL.md +10 -10
- 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: e3c86744b3644ad258aa39eff8ff11b55df1cc933003276126f7b1b3d9952d91
|
|
4
|
+
data.tar.gz: e0a59266d59248a00e092c96b85978bde02bf19d1dd85f6b382addc3ca9fb32b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dbcc3f50c232a8cf847db8804abc4af17140f884c051b414fea6f6cd56d0f93127c2e002b2856c255305dc3685b236329dd2a58dc6e552f99dc11b6d441ccd4d
|
|
7
|
+
data.tar.gz: 83f7161eebc10dd01d0634fa4eb022c7d5afa5f8ace26690bfa5022c5fa624409f167497dcaf9c2ef2cd3b05ea452dccf7124bd64581c5cba456b162cad2fc97
|
data/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Built for RSpec, but could be ported to other test frameworks (Minitest, etc.).
|
|
|
8
8
|
`react_component_props`, an optional React-specific helper, reads props out of
|
|
9
9
|
rendered mounts.
|
|
10
10
|
|
|
11
|
-
React is the case this gem was built for, not a requirement. `
|
|
11
|
+
React is the case this gem was built for, not a requirement. `match_ts_schema`
|
|
12
12
|
checks any payload against any exported TypeScript type, so a Stimulus
|
|
13
13
|
controller or a plain fetch client works the same way.
|
|
14
14
|
|
|
@@ -101,7 +101,7 @@ it "matches AccountPayload" do
|
|
|
101
101
|
|
|
102
102
|
expect(response).to be_successful
|
|
103
103
|
expect(response.parsed_body["accounts"])
|
|
104
|
-
.to
|
|
104
|
+
.to match_ts_schema("app/javascript/types/account.ts", "AccountPayload")
|
|
105
105
|
end
|
|
106
106
|
```
|
|
107
107
|
|
|
@@ -134,29 +134,32 @@ describe "the props handed to RoleMatrix" do
|
|
|
134
134
|
|
|
135
135
|
expect(response).to be_successful
|
|
136
136
|
props = react_component_props("RoleMatrix")
|
|
137
|
-
expect(props).to
|
|
137
|
+
expect(props).to match_ts_schema(role_matrix, "RoleMatrixProps")
|
|
138
138
|
end
|
|
139
139
|
end
|
|
140
140
|
```
|
|
141
141
|
|
|
142
|
-
`
|
|
142
|
+
`match_ts_schema` accepts a hash or an array of hashes. Given an array, it
|
|
143
143
|
validates every item and fails on an empty one — in both directions, so
|
|
144
|
-
`to_not
|
|
144
|
+
`to_not match_ts_schema` does not pass vacuously either. A page that stopped
|
|
145
145
|
rendering the component fails rather than passing silently. The alternative
|
|
146
|
-
construction, `expect(props).to all
|
|
147
|
-
array.
|
|
146
|
+
construction, `expect(props).to all match_ts_schema(...)`, would pass on an
|
|
147
|
+
empty array.
|
|
148
148
|
|
|
149
149
|
## API
|
|
150
150
|
|
|
151
151
|
| Call | Returns |
|
|
152
152
|
| --------------------------------------- | ----------------------------------------------------------- |
|
|
153
153
|
| `TsSchemaSpec.schema_for(path, type)` | a `JSONSchemer` schema scoped to that exported type |
|
|
154
|
-
| `
|
|
154
|
+
| `match_ts_schema(path, type)` | matcher; validates a hash, or every item of an array |
|
|
155
155
|
| `react_component_props(name[, html])` | array of props hashes, one per mount |
|
|
156
156
|
| `TsSchemaSpec::Skill.check!(root)` | raises if the installed skill is stale or missing |
|
|
157
157
|
| `TsSchemaSpec.configure` | sets `tsconfig` and extra generator arguments |
|
|
158
158
|
| `TsSchemaSpec.clear_cache!` | drops the generated-schema cache |
|
|
159
159
|
|
|
160
|
+
`match_ts_schema` was called `match_schema` before 0.6.1. The old name still
|
|
161
|
+
works but is deprecated and will be removed.
|
|
162
|
+
|
|
160
163
|
`path` is resolved from wherever the suite runs, which is the Rails root in
|
|
161
164
|
practice. Name the type at the assertion, so an example says which type it is
|
|
162
165
|
checking. When several examples read the same source, bind the path to a local
|
|
@@ -208,8 +211,8 @@ generator, a shared JSON file, whatever suits your repo. That is outside this
|
|
|
208
211
|
gem's scope, but with one in place, and a record for each value in the
|
|
209
212
|
example, a drifted union fails here rather than in the browser.
|
|
210
213
|
|
|
211
|
-
**Pass the whole collection.** `
|
|
212
|
-
on an empty one. Avoid `expect(props).to all
|
|
214
|
+
**Pass the whole collection.** `match_ts_schema` validates every item and fails
|
|
215
|
+
on an empty one. Avoid `expect(props).to all match_ts_schema(...)`, which
|
|
213
216
|
passes on an empty array, hiding an issue with generation.
|
|
214
217
|
|
|
215
218
|
**Assert the response is successful first.** Otherwise a redirect or a 500
|
data/lib/ts_schema_spec/rspec.rb
CHANGED
|
@@ -66,7 +66,7 @@ module TsSchemaSpec
|
|
|
66
66
|
end
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
-
RSpec::Matchers.define :
|
|
69
|
+
RSpec::Matchers.define :match_ts_schema do |source, type|
|
|
70
70
|
def validation_errors(actual, source, type)
|
|
71
71
|
schema = TsSchemaSpec.schema_for(source, type)
|
|
72
72
|
@errors = TsSchemaSpec::Matching.errors(schema, actual)
|
|
@@ -81,7 +81,7 @@ RSpec::Matchers.define :match_schema do |source, type|
|
|
|
81
81
|
|
|
82
82
|
Usually the component was not rendered on the page, or no records
|
|
83
83
|
existed for the endpoint to serialize. If an empty result is what you
|
|
84
|
-
meant to assert, use `be_empty` or `eq([])` —
|
|
84
|
+
meant to assert, use `be_empty` or `eq([])` — match_ts_schema on an empty
|
|
85
85
|
collection checks nothing.
|
|
86
86
|
MSG
|
|
87
87
|
end
|
|
@@ -121,3 +121,12 @@ RSpec::Matchers.define :match_schema do |source, type|
|
|
|
121
121
|
"expected the payload not to match #{type} (#{source}), but it did:\n#{JSON.pretty_generate(actual)}"
|
|
122
122
|
end
|
|
123
123
|
end
|
|
124
|
+
|
|
125
|
+
module RSpec
|
|
126
|
+
module Matchers
|
|
127
|
+
def match_schema(...)
|
|
128
|
+
RSpec.deprecate("match_schema", replacement: "match_ts_schema")
|
|
129
|
+
match_ts_schema(...)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ts-schema-spec
|
|
3
3
|
description: >
|
|
4
|
-
Write or update RSpec tests that use
|
|
4
|
+
Write or update RSpec tests that use match_ts_schema to verify a Rails
|
|
5
5
|
endpoint's payload matches the TypeScript type that consumes it. TRIGGER
|
|
6
6
|
automatically (without being asked) whenever data crossing from Ruby to
|
|
7
7
|
TypeScript is added or changed: adding a controller action; changing
|
|
@@ -9,7 +9,7 @@ description: >
|
|
|
9
9
|
associations); adding or renaming a key in a render json: response or in
|
|
10
10
|
props handed to a component; converting a Rails-mounted component from .jsx
|
|
11
11
|
to .tsx; deleting a component's propTypes; or rendering an already-typed
|
|
12
|
-
component from an action that has no
|
|
12
|
+
component from an action that has no match_ts_schema spec. React is the common
|
|
13
13
|
case, not a requirement — a Stimulus controller or a plain fetch client
|
|
14
14
|
reading the payload counts the same. Invoked as /ts-schema-spec.
|
|
15
15
|
---
|
|
@@ -19,15 +19,15 @@ something changes, and including where there is no Ruby diff at all. Two rules
|
|
|
19
19
|
keep that from multiplying:
|
|
20
20
|
|
|
21
21
|
- **Repeated mounts of one component are a single example.**
|
|
22
|
-
`react_component_props` returns every mount and `
|
|
22
|
+
`react_component_props` returns every mount and `match_ts_schema` checks each.
|
|
23
23
|
- **Assert on the component Rails mounts.** A child receiving props from its
|
|
24
24
|
parent is covered transitively; use the parent's props type.
|
|
25
25
|
|
|
26
26
|
## Step 0 — Already covered?
|
|
27
27
|
|
|
28
|
-
Covered = the spec for **the action rendering it** has a `
|
|
29
|
-
on that component. A sibling component, or the same component from
|
|
30
|
-
action, is not coverage. Covered → stop.
|
|
28
|
+
Covered = the spec for **the action rendering it** has a `match_ts_schema`
|
|
29
|
+
example on that component. A sibling component, or the same component from
|
|
30
|
+
another action, is not coverage. Covered → stop.
|
|
31
31
|
|
|
32
32
|
## Step 1 — Find the TypeScript consumer
|
|
33
33
|
|
|
@@ -71,7 +71,7 @@ implying the spec covers them.
|
|
|
71
71
|
| `react_component` | `react_component_props("ComponentName")` |
|
|
72
72
|
|
|
73
73
|
`react_component_props` returns **an array**, one entry per mount, and needs
|
|
74
|
-
`render_views`. Pass it straight to `
|
|
74
|
+
`render_views`. Pass it straight to `match_ts_schema`: it validates every entry
|
|
75
75
|
and fails on an empty collection. Never wrap it in `all`, which iterates zero
|
|
76
76
|
times on an empty array and asserts nothing.
|
|
77
77
|
|
|
@@ -87,7 +87,7 @@ describe "the props handed to MyComponent" do
|
|
|
87
87
|
|
|
88
88
|
expect(response).to be_successful
|
|
89
89
|
props = react_component_props("MyComponent")
|
|
90
|
-
expect(props).to
|
|
90
|
+
expect(props).to match_ts_schema("app/javascript/MyComponent.tsx", "MyComponentProps")
|
|
91
91
|
end
|
|
92
92
|
end
|
|
93
93
|
```
|
|
@@ -100,12 +100,12 @@ Rules:
|
|
|
100
100
|
- Build multiple records with different traits, so optional fields, enum values
|
|
101
101
|
and nil associations are actually exercised. Use existing factory traits.
|
|
102
102
|
- Assert `response` is successful before asserting shape.
|
|
103
|
-
- Let `
|
|
103
|
+
- Let `match_ts_schema` do the shape checking; no hand-written field assertions.
|
|
104
104
|
- Several components in one action: an example each, or one example marked
|
|
105
105
|
`:aggregate_failures` if rendering the page is expensive — without it the
|
|
106
106
|
first mismatch hides the rest.
|
|
107
107
|
- Do not write a spec that only checks `response.status`, and do not duplicate
|
|
108
|
-
an existing `
|
|
108
|
+
an existing `match_ts_schema` for the same action.
|
|
109
109
|
- **When it fails, fix Rails.** The type is the consumer's contract: if the
|
|
110
110
|
component needs a field, the payload is wrong. Loosening the type is always
|
|
111
111
|
the quicker route to green, and it is how this stops catching anything.
|