smartest 0.6.1 → 0.6.2.alpha1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 60329ddc39f19de585b5eca7624d830188201867f12acc4c5d455086770c6e55
4
- data.tar.gz: e3b94d9094456040638436153dea247c90a09df900f73fac8a446e230cf121e6
3
+ metadata.gz: 5eb327b86d510c4cf4e8ebdf31fcd7be3fe1b78c9109528ac99af08c3af1f9e4
4
+ data.tar.gz: 7937fde33ac1e62adb5f4bc39b54b938138af915008fcc350742f66420df5848
5
5
  SHA512:
6
- metadata.gz: 47ba0913bb68d3015905baa12677ed6ac2f812723935a546d0f6f9c0e0154776fb72a9e2844a77833743c97a8363066c7199c767022022b17f169415c48b9001
7
- data.tar.gz: 86f5bdd1c49b6f461408c5ee21075a0a75406e8f6c139f6146871fcbd0dea6644129e63339602851f2a122ac075d6bcc7b96d95d69224e446dc5e6031c951cf9
6
+ metadata.gz: 8b011cea66dfec275fe790c0ced127819c23a18e70878bb9f4519d0e87b05e47c06b2b7bbc5de771ff3782cbb032409cb57be50ca2a2211fe79150a15a46b224
7
+ data.tar.gz: a77eb822acc8bb07f4f3b38a5f09bf3ca573fabb4542bc03b2e49120b110aad749042fe8004e38c733b0f4629f77673c371011382d7ab2d5e0881364c7a48595
data/README.md CHANGED
@@ -227,11 +227,11 @@ smartest/matchers/playwright_matcher.rb
227
227
  smartest/example_browser_test.rb
228
228
  ```
229
229
 
230
- It also registers `PlaywrightFixture` and `PlaywrightMatcher`, adds
231
- `playwright-ruby-client` to the Gemfile test group, runs `bundle install`, runs
232
- `npm init --yes` when no `package.json` exists yet, runs
233
- `npm install playwright --save-dev`, and downloads Chromium with
234
- `./node_modules/.bin/playwright install`.
230
+ It also registers `PlaywrightFixture` and `PlaywrightMatcher`, keeps or adds
231
+ `smartest` in the Gemfile, adds `playwright-ruby-client` to the Gemfile test
232
+ group, runs `bundle install`, runs `npm init --yes` when no `package.json`
233
+ exists yet, runs `npm install playwright --save-dev`, and downloads Chromium
234
+ with `./node_modules/.bin/playwright install`.
235
235
 
236
236
  Run the generated browser example with:
237
237
 
@@ -239,6 +239,9 @@ Run the generated browser example with:
239
239
  bundle exec smartest smartest/example_browser_test.rb
240
240
  ```
241
241
 
242
+ The generated Playwright examples include `# @type [Playwright::Page] page`
243
+ before the test block so RubyMine can complete methods on the `page` fixture.
244
+
242
245
  ## Rails browser quick start
243
246
 
244
247
  Initialize a Rails browser-test scaffold:
@@ -328,6 +331,10 @@ end
328
331
 
329
332
  This makes fixture usage explicit and avoids relying on positional argument order.
330
333
 
334
+ Smartest also ships RBS signatures for the public DSL, including `test`,
335
+ `around_suite`, `around_test`, `fixture`, `suite_fixture`, `on_teardown`,
336
+ `expect`, and hook registration methods such as `use_fixture`.
337
+
331
338
  ## Skipping and pending tests
332
339
 
333
340
  Use `skip` at the start of a test when the rest of the body should not run:
@@ -63,6 +63,7 @@ module Smartest
63
63
 
64
64
  require "test_helper"
65
65
 
66
+ # @type [Playwright::Page] page
66
67
  test("finds the smartest gem on RubyGems") do |page:|
67
68
  page.goto("https://rubygems.org/")
68
69
  page.locator("input[name='query']").fill("smartest")
@@ -147,16 +148,33 @@ module Smartest
147
148
  path = File.join(@root, "Gemfile")
148
149
  exists = File.exist?(path)
149
150
  contents = exists ? File.read(path) : "source \"https://rubygems.org\"\n"
151
+ updated = contents.dup
150
152
 
151
- if contents.match?(/gem ["']playwright-ruby-client["']/)
153
+ unless gem_present?(updated, "smartest")
154
+ updated = append_gem(updated, 'gem "smartest"')
155
+ end
156
+
157
+ if gem_present?(updated, "playwright-ruby-client")
152
158
  @output.puts "exist Gemfile playwright-ruby-client"
153
- return
159
+ else
160
+ updated = append_gem(updated, 'gem "playwright-ruby-client", group: :test')
154
161
  end
155
162
 
163
+ if updated != contents
164
+ File.write(path, updated)
165
+ @output.puts(exists ? "update Gemfile" : "create Gemfile")
166
+ end
167
+ end
168
+
169
+ def append_gem(contents, line)
156
170
  separator = contents.end_with?("\n") ? "" : "\n"
157
- updated = "#{contents}#{separator}\ngem \"playwright-ruby-client\", group: :test\n"
158
- File.write(path, updated)
159
- @output.puts(exists ? "update Gemfile" : "create Gemfile")
171
+ "#{contents}#{separator}\n#{line}\n"
172
+ end
173
+
174
+ def gem_present?(contents, name)
175
+ contents.each_line.any? do |line|
176
+ line.match?(/\A\s*gem\s+["']#{Regexp.escape(name)}["']/)
177
+ end
160
178
  end
161
179
 
162
180
  def install_dependencies
@@ -118,6 +118,7 @@ module Smartest
118
118
 
119
119
  require "test_helper"
120
120
 
121
+ # @type [Playwright::Page] page
121
122
  test("loads the Rails application") do |page:|
122
123
  response = page.goto("/")
123
124
 
@@ -198,16 +199,33 @@ module Smartest
198
199
  path = File.join(@root, "Gemfile")
199
200
  exists = File.exist?(path)
200
201
  contents = exists ? File.read(path) : "source \"https://rubygems.org\"\n"
202
+ updated = contents.dup
201
203
 
202
- if contents.match?(/gem ["']playwright-ruby-client["']/)
204
+ unless gem_present?(updated, "smartest")
205
+ updated = append_gem(updated, 'gem "smartest"')
206
+ end
207
+
208
+ if gem_present?(updated, "playwright-ruby-client")
203
209
  @output.puts "exist Gemfile playwright-ruby-client"
204
- return
210
+ else
211
+ updated = append_gem(updated, 'gem "playwright-ruby-client", group: :test')
205
212
  end
206
213
 
214
+ if updated != contents
215
+ File.write(path, updated)
216
+ @output.puts(exists ? "update Gemfile" : "create Gemfile")
217
+ end
218
+ end
219
+
220
+ def append_gem(contents, line)
207
221
  separator = contents.end_with?("\n") ? "" : "\n"
208
- updated = "#{contents}#{separator}\ngem \"playwright-ruby-client\", group: :test\n"
209
- File.write(path, updated)
210
- @output.puts(exists ? "update Gemfile" : "create Gemfile")
222
+ "#{contents}#{separator}\n#{line}\n"
223
+ end
224
+
225
+ def gem_present?(contents, name)
226
+ contents.each_line.any? do |line|
227
+ line.match?(/\A\s*gem\s+["']#{Regexp.escape(name)}["']/)
228
+ end
211
229
  end
212
230
 
213
231
  def install_dependencies
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Smartest
4
- VERSION = "0.6.1"
4
+ VERSION = "0.6.2.alpha1"
5
5
  end
data/sig/smartest.rbs ADDED
@@ -0,0 +1,163 @@
1
+ # Smartest public DSL signatures for editors and type-aware tooling.
2
+
3
+ module Smartest
4
+ VERSION: String
5
+
6
+ def self.suite: () -> Suite
7
+ def self.reset!: () -> Suite
8
+ def self.disable_autorun!: () -> true
9
+ def self.autorun_disabled?: () -> bool
10
+ def self.register_autorun!: () -> nil
11
+ def self.install_dsl!: () -> untyped
12
+ def self.fatal_exception?: (Exception) -> bool
13
+
14
+ module DSL
15
+ private
16
+
17
+ # Define a named test. Fixture values are requested from the block with
18
+ # required keyword arguments, for example `do |page:|`.
19
+ def test: (String | Symbol, **untyped) { (*untyped, **untyped) -> untyped } -> void
20
+
21
+ # Wrap the whole suite. The block receives a run target and must call
22
+ # `suite.run` exactly once.
23
+ def around_suite: () { (SuiteRun) -> untyped } -> void
24
+
25
+ # Wrap tests registered after this hook in the same file. The block receives
26
+ # a run target and must call `test.run` exactly once.
27
+ def around_test: () { (TestRun) -> untyped } -> void
28
+ end
29
+
30
+ module Expectations
31
+ private
32
+
33
+ def expect: (?untyped) ?{ () -> untyped } -> ExpectationTarget
34
+ end
35
+
36
+ module Matchers
37
+ private
38
+
39
+ def eq: (untyped) -> Matcher
40
+ def include: (untyped) -> Matcher
41
+ def start_with: (*untyped) -> Matcher
42
+ def end_with: (*untyped) -> Matcher
43
+ def be_a: (Class | Module) -> Matcher
44
+ def be_an: (Class | Module) -> Matcher
45
+ def be_nil: () -> Matcher
46
+ def match: (Regexp | String) -> Matcher
47
+ def contain_exactly: (*untyped) -> Matcher
48
+ def match_array: (Array[untyped]) -> Matcher
49
+ def raise_error: (*untyped) -> Matcher
50
+ def change: () { () -> untyped } -> Matcher
51
+ end
52
+
53
+ module ConstantStubHelpers
54
+ private
55
+
56
+ def with_stub_const: (String | Symbol, untyped) { () -> untyped } -> untyped
57
+ end
58
+
59
+ class Fixture
60
+ # Define a test-scoped fixture by default. Fixture dependencies are requested
61
+ # from the block with required keyword arguments.
62
+ def self.fixture: (String | Symbol, ?scope: Symbol) { (*untyped, **untyped) -> untyped } -> FixtureDefinition
63
+
64
+ # Define a suite-scoped fixture shared by all tests in the suite.
65
+ def self.suite_fixture: (String | Symbol) { (*untyped, **untyped) -> untyped } -> FixtureDefinition
66
+
67
+ def self.fixture_definitions: () -> Hash[Symbol, FixtureDefinition]
68
+
69
+ private
70
+
71
+ def on_teardown: () { () -> untyped } -> untyped
72
+ def simple_stub_any_instance_of: (Class | Module, String | Symbol) { (*untyped, **untyped) -> untyped } -> SimpleStub
73
+ def simple_stub: (untyped, String | Symbol) { (*untyped, **untyped) -> untyped } -> SimpleStub
74
+ end
75
+
76
+ class AroundSuiteContext
77
+ private
78
+
79
+ def use_fixture: (Class | Module) -> untyped
80
+ def use_matcher: (Module) -> untyped
81
+ def around_test: () { (TestRun) -> untyped } -> void
82
+ def with_stub_const: (String | Symbol, untyped) { () -> untyped } -> untyped
83
+ end
84
+
85
+ class AroundTestContext
86
+ private
87
+
88
+ def use_fixture: (Class | Module) -> untyped
89
+ def use_matcher: (Module) -> untyped
90
+ def use_helper: (Module) -> untyped
91
+ def skip: (?String) -> void
92
+ def pending: (?String) -> void
93
+ def with_stub_const: (String | Symbol, untyped) { () -> untyped } -> untyped
94
+ end
95
+
96
+ class ExecutionContext
97
+ include Expectations
98
+ include Matchers
99
+ include ConstantStubHelpers
100
+
101
+ private
102
+
103
+ def skip: (?String) -> void
104
+ def pending: (?String) -> void
105
+ end
106
+
107
+ class SuiteRun
108
+ attr_reader result: untyped
109
+
110
+ def run: () -> untyped
111
+ def ran?: () -> bool
112
+ end
113
+
114
+ class TestRun
115
+ attr_reader result: untyped
116
+
117
+ def run: () -> untyped
118
+ def ran?: () -> bool
119
+ end
120
+
121
+ class ExpectationTarget
122
+ def to: (untyped) -> self
123
+ def not_to: (untyped) -> self
124
+ end
125
+
126
+ class Matcher
127
+ def and: (untyped) -> Matcher
128
+ def or: (untyped) -> Matcher
129
+ def description: () -> String
130
+ end
131
+
132
+ class FixtureDefinition
133
+ attr_reader name: Symbol
134
+ attr_reader block: Proc
135
+ attr_reader location: Thread::Backtrace::Location?
136
+ attr_reader scope: Symbol
137
+ attr_reader dependencies: Array[Symbol]
138
+ end
139
+
140
+ class SimpleStub
141
+ def apply: () -> self
142
+ def reset: () -> nil
143
+ end
144
+
145
+ class Suite
146
+ end
147
+ end
148
+
149
+ module Kernel
150
+ private
151
+
152
+ # Define a named Smartest test. Fixture values are requested with keyword
153
+ # block parameters, for example `test("opens page") do |page:|`.
154
+ def test: (String | Symbol, **untyped) { (*untyped, **untyped) -> untyped } -> void
155
+
156
+ # Register a suite hook. The block receives a run target and must call
157
+ # `suite.run` exactly once.
158
+ def around_suite: () { (Smartest::SuiteRun) -> untyped } -> void
159
+
160
+ # Register a test hook for tests declared after this hook in the same file.
161
+ # The block receives a run target and must call `test.run` exactly once.
162
+ def around_test: () { (Smartest::TestRun) -> untyped } -> void
163
+ end
@@ -82,6 +82,12 @@ test("registers fixture classes with use_fixture") do |registered_user_name:|
82
82
  expect(registered_user_name).to eq("Alice")
83
83
  end
84
84
 
85
+ test("gemspec packages RBS signatures") do
86
+ spec = Gem::Specification.load(File.expand_path("../smartest.gemspec", __dir__))
87
+
88
+ expect(spec.files).to include("sig/smartest.rbs")
89
+ end
90
+
85
91
  test("runs a registered test") do
86
92
  suite = Smartest::Suite.new
87
93
  suite.tests.add(SmartestSelfTest.test_case("factorial", proc { expect(1 * 2 * 3).to eq(6) }))
@@ -2150,6 +2156,7 @@ test("cli browser init generator creates Playwright scaffold and installation co
2150
2156
  expect(status).to eq(0)
2151
2157
  example_browser_test = File.read(File.join(dir, "smartest/example_browser_test.rb"))
2152
2158
  expect(example_browser_test).to include("finds the smartest gem on RubyGems")
2159
+ expect(example_browser_test).to include("# @type [Playwright::Page] page")
2153
2160
  expect(example_browser_test).to include('expect(page).to have_url("https://rubygems.org/gems/smartest")')
2154
2161
  expect(example_browser_test).to include('expect(page.locator("h1")).to have_text("smartest")')
2155
2162
  expect(example_browser_test).not_to include("0.3.0.alpha1")
@@ -2166,6 +2173,8 @@ test("cli browser init generator creates Playwright scaffold and installation co
2166
2173
  expect(helper_contents).to include("use_matcher PredicateMatcher\n use_fixture PlaywrightFixture\n use_matcher PlaywrightMatcher\n suite.run")
2167
2174
 
2168
2175
  gemfile_contents = File.read(File.join(dir, "Gemfile"))
2176
+ expect(gemfile_contents).to include('gem "smartest"')
2177
+ expect(gemfile_contents.scan(/^\s*gem ["']smartest["']/).length).to eq(1)
2169
2178
  expect(gemfile_contents).to include('gem "playwright-ruby-client", group: :test')
2170
2179
  expect(commands).to eq(
2171
2180
  [
@@ -2183,6 +2192,27 @@ test("cli browser init generator creates Playwright scaffold and installation co
2183
2192
  end
2184
2193
  end
2185
2194
 
2195
+ test("cli browser init generator keeps smartest in Gemfile when adding Playwright") do
2196
+ Dir.mktmpdir do |dir|
2197
+ File.write(File.join(dir, "Gemfile"), <<~RUBY)
2198
+ source "https://rubygems.org"
2199
+ RUBY
2200
+
2201
+ generator = Smartest::InitBrowserGenerator.new(
2202
+ root: dir,
2203
+ output: StringIO.new,
2204
+ command_runner: ->(_command, chdir:) { true }
2205
+ )
2206
+
2207
+ status = generator.run
2208
+
2209
+ expect(status).to eq(0)
2210
+ gemfile_contents = File.read(File.join(dir, "Gemfile"))
2211
+ expect(gemfile_contents).to include('gem "smartest"')
2212
+ expect(gemfile_contents).to include('gem "playwright-ruby-client", group: :test')
2213
+ end
2214
+ end
2215
+
2186
2216
  test("cli browser init generator runs installation commands outside the current Bundler environment") do
2187
2217
  require "bundler"
2188
2218
 
@@ -2423,6 +2453,7 @@ test("cli rails init generator creates Rails browser scaffold and installation c
2423
2453
  expect(rails_fixture).to include("fixture :page do |browser_context:|")
2424
2454
 
2425
2455
  example_test = File.read(File.join(dir, "smartest/example_rails_system_test.rb"))
2456
+ expect(example_test).to include("# @type [Playwright::Page] page")
2426
2457
  expect(example_test).to include('test("loads the Rails application") do |page:|')
2427
2458
  expect(example_test).to include('response = page.goto("/")')
2428
2459
  expect(example_test).to include("expect(response.status).to be_between(200, 599)")
@@ -2432,6 +2463,8 @@ test("cli rails init generator creates Rails browser scaffold and installation c
2432
2463
  expect(helper_contents).not_to include("Smartest::SimpleStub")
2433
2464
 
2434
2465
  gemfile_contents = File.read(File.join(dir, "Gemfile"))
2466
+ expect(gemfile_contents).to include('gem "smartest"')
2467
+ expect(gemfile_contents.scan(/^\s*gem ["']smartest["']/).length).to eq(1)
2435
2468
  expect(gemfile_contents).to include('gem "playwright-ruby-client", group: :test')
2436
2469
  expect(commands).to eq(
2437
2470
  [
@@ -2551,6 +2584,29 @@ test("cli rails init generator runs installation commands outside the current Bu
2551
2584
  end
2552
2585
  end
2553
2586
 
2587
+ test("cli rails init generator keeps smartest in Gemfile when adding Playwright") do
2588
+ Dir.mktmpdir do |dir|
2589
+ File.write(File.join(dir, "Gemfile"), <<~RUBY)
2590
+ source "https://rubygems.org"
2591
+
2592
+ gem "rails"
2593
+ RUBY
2594
+
2595
+ generator = Smartest::InitRailsGenerator.new(
2596
+ root: dir,
2597
+ output: StringIO.new,
2598
+ command_runner: ->(_command, chdir:) { true }
2599
+ )
2600
+
2601
+ status = generator.run
2602
+
2603
+ expect(status).to eq(0)
2604
+ gemfile_contents = File.read(File.join(dir, "Gemfile"))
2605
+ expect(gemfile_contents).to include('gem "smartest"')
2606
+ expect(gemfile_contents).to include('gem "playwright-ruby-client", group: :test')
2607
+ end
2608
+ end
2609
+
2554
2610
  test("cli rails init generator skips browser install when requested by environment") do
2555
2611
  SmartestSelfTest.with_env("SMARTEST_SKIP_BROWSER_DOWNLOAD" => "1") do
2556
2612
  Dir.mktmpdir do |dir|
data/smartest.gemspec CHANGED
@@ -42,6 +42,7 @@ Gem::Specification.new do |spec|
42
42
  "SMARTEST_DESIGN.md",
43
43
  "exe/*",
44
44
  "lib/**/*.rb",
45
+ "sig/**/*.rbs",
45
46
  "smartest.gemspec",
46
47
  "smartest/**/*.rb"
47
48
  ]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smartest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.6.2.alpha1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yusuke Iwaki
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-05-10 00:00:00.000000000 Z
11
+ date: 2026-05-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -78,6 +78,7 @@ files:
78
78
  - lib/smartest/test_run.rb
79
79
  - lib/smartest/test_run_state.rb
80
80
  - lib/smartest/version.rb
81
+ - sig/smartest.rbs
81
82
  - smartest.gemspec
82
83
  - smartest/simple_stub_test.rb
83
84
  - smartest/smartest_test.rb
@@ -103,9 +104,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
103
104
  version: '2.7'
104
105
  required_rubygems_version: !ruby/object:Gem::Requirement
105
106
  requirements:
106
- - - ">="
107
+ - - ">"
107
108
  - !ruby/object:Gem::Version
108
- version: '0'
109
+ version: 1.3.1
109
110
  requirements: []
110
111
  rubygems_version: 3.4.19
111
112
  signing_key: