Reduces RSpec startup time by avoiding work that the selected spec does
not need.
The main changes are:
- Mark development/test dependencies as require: false where they can be
loaded safely at their point of use.
- Avoid loading Capybara, the Playwright driver, system helpers, and
page objects unless the selected specs need them.
- Autoload several large spec helper modules and load Rails tasks only
for task specs.
- Load OAuth providers, schema libraries, API documentation support, and
QR-code support at their actual call sites.
- Preload production dependencies before workers fork to preserve
copy-on-write sharing.
- Register parallel_tests Rake tasks only in local environments so
production bundles do not require test-only gems.
- Make rbtrace opt-in via RBTRACE=1.
- Avoid creating an unrelated top-level fixture in user_spec.rb.
- Explicitly load Capybara in the nginx integration spec, which sits
outside the system spec directory.
The new lazy support files work as follows:
- lazy_fabricators.rb indexes core fabricator definitions without
executing every file. When an unregistered fabricator is first
requested, only the file defining it is loaded. Plugin suites retain
eager fabricator loading for compatibility.
- lazy_faker.rb intercepts a missing Faker constant, loads the full
Faker gem on first use, and retries the lookup.
- lazy_pry.rb provides a temporary pry method that loads pry and
pry-rails on first use, then delegates to the real implementation.
On my machine, startup for bin/rspec spec/models/user_spec.rb:20
decreased from approximately 2.1 seconds to 1.1 seconds—a reduction of
roughly 47%.
Reduces RSpec startup time by avoiding work that the selected spec does
not need.
The main changes are:
- Mark development/test dependencies as require: false where they can be
loaded safely at their point of use.
- Avoid loading Capybara, the Playwright driver, system helpers, and
page objects unless system or request specs need them.
- Autoload several large spec helper modules and load Rails tasks only
for task specs.
- Load OAuth providers, schema libraries, API documentation support, and
QR-code support at their actual call sites.
- Make rbtrace opt-in via RBTRACE=1.
- Avoid creating an unrelated top-level fixture in user_spec.rb.
The new lazy support files work as follows:
- lazy_fabricators.rb indexes core fabricator definitions without
executing every file. When an unregistered fabricator is first
requested, only the file defining it is loaded. Plugin suites retain
eager fabricator loading for compatibility.
- lazy_faker.rb intercepts a missing Faker constant, loads the full
Faker gem on first use, and retries the lookup.
- lazy_pry.rb provides a temporary pry method that loads pry and
pry-rails on first use, then delegates to the real implementation.
On my machine, startup for bin/rspec spec/models/user_spec.rb:20
decreased from approximately 2.1 seconds to 1.1 seconds—a reduction of
roughly 47%.
Adds **voice** (formerly the standalone `resenha` plugin) as a core
plugin: Discord-style voice and video rooms powered by WebRTC —
sidebar-first rooms, direct calls, optional LiveKit SFU routing,
chat-thread integration, live subtitles, and recordings.
## Rename
The plugin was renamed `resenha` → `voice` as part of the move (`module
::Voice`, `plugins/voice`, `voice_*` settings and tables, mounted at
`/voice`), consistent with `plugins/chat`.
Sites that ran the plugin under its old name are converted by a
post-deploy migration: table/index/trigger renames, `resenha_*` →
`voice_*` site settings, reviewable types, chat thread custom fields,
the badge grouping, and rebake marks for posts/chat messages whose
cooked HTML embeds `/resenha/` links. Fresh installs create the
`voice_*` schema directly. The `resenha_invitation` notification type
keeps its id (1000) under the new `voice_invitation` name.
## Size
Large media assets (wasm noise-suppression engines, ML models, vendored
SDK bundles, ~75MB) are not in this tree — they ship in the
[discourse_voice_assets](https://github.com/discourse/discourse_voice_assets)
gem and are served from a gem-version-stamped path, so the plugin adds
~2.5MB of source.
## Also included
- `DEV: Fix bin/lint crash on unbundled plugin paths` —
`bundled_plugins` is a `Set`, which has no `#exclude?` without
ActiveSupport, so linting any unbundled `plugins/` path raised
`NoMethodError`.
---------
Co-authored-by: Gabriel Grubba <70247653+Grubba27@users.noreply.github.com>
Co-authored-by: Penar Musaraj <pmusaraj@gmail.com>
Co-authored-by: Keegan George <kgeorge13@gmail.com>
Co-authored-by: David Taylor <david@taylorhq.com>
Co-authored-by: Renato Atilio <renatoat@gmail.com>
Co-authored-by: dupless54 <126967564+dupless54@users.noreply.github.com>
Co-authored-by: Arpit Jalan <arpit@arpitjalan.com>
Reported at
https://meta.discourse.org/t/translation-silently-truncated-when-json-stream-parsing-breaks-no-error-raised/407251
## The bug
Some providers stream structured output whose string values were
unescaped by an outer JSON parse, so real newlines appear inside string
values. When that happened, `JsonStreamingTracker` had two failure
modes:
- It marked the stream broken and `StructuredOutput` fell back to
`BestEffortJsonParser`, whose extraction regex (`[^"]+`) cut the value
at the first escaped quote and left `\n` sequences as literal text. A
2,000-char translation could come back as ~50 chars, cut right before
the first quoted word — exactly what the report shows.
- Its escape-and-resume hack (`String#dump` + buffer-growth offset)
miscomputed the resume index whenever the chunk contained non-ASCII,
quotes, or backslashes, silently duplicating or corrupting content
**without ever marking the stream broken**.
Testing a realistic corrupted payload across chunk sizes 1–60: 31
produced the truncated fallback, 27 produced silent corruption, 1
raised, and only 1 came out correct. Either way the result was persisted
as a successful translation with nothing in the logs.
## The fix
Replace the hand-rolled parsing with two gems and keep only glue:
- **json_completer** (pure Ruby): `JsonStreamingTracker` now feeds the
cumulative buffer — with control characters re-escaped — to an
incremental, truncation-tolerant parser and notifies consumers of
changed keys. The corrupted payloads above stream correctly at every
chunk size, so the broken-stream path is only reached for responses that
aren't JSON at all.
- **smarter_json**: `BestEffortJsonParser` becomes a three-attempt chain
(strict-with-completion → control-chars re-escaped → lenient) covering
the quirk shapes the old regexes handled: single quotes, unquoted keys,
markdown fences, prose-wrapped JSON.
This deletes the vendored 668-line SAX parser, the resume hack, and all
manual regex extraction (net −694 lines), and adds a log warning
whenever a response falls back to best-effort parsing.
## Behavior changes
- Scalars now stream progressively: mid-stream
`read_buffered_property(:number)` returns the digits buffered so far
instead of `nil`. Consumers act on final values, so this only affects
mid-stream reads.
- Arrays of objects stream partial objects mid-stream instead of
returning `nil` until finish.
- A trailing comma in an array reads as a `nil` placeholder slot until
the next element arrives.
- Partial tool calls surface a few more progressive updates (the openai
endpoint spec count moved 128 → 134); values still only ever grow.
## Tests
- Regression specs for the report: unescaped control characters with
escaped quotes/emoji streamed across chunk boundaries, fenced +
unescaped responses, truncated JSON, numeric casting.
- 916 examples green across `completions/`, `translation/`,
`modules/ai_helper/`, and `utils/`.
GitHub oneboxes and the discourse-github plugin talked to GitHub's REST
and
GraphQL API with no rate-limit awareness. On busy instances this
exhausted
GitHub's limits (60 requests/hour unauthenticated, 5000 authenticated),
and
because there was no backoff every render kept hitting GitHub and
re-failing
-- which GitHub's docs warn can get an integration banned. The recently
added PR-status onebox multiplied the number of calls and made it far
worse.
GitHub access was also fragmented: the core onebox engines used OpenURI,
the
discourse-github plugin used Octokit, and the discourse-ai bot tools
used
FinalDestination::HTTP -- three HTTP stacks, three tokens, and
inconsistent
(or entirely missing) error and rate-limit handling.
This introduces a single client, Discourse::GithubApi, that every GitHub
data-API request now flows through. It is built on Faraday with the
SSRF-safe
FinalDestination adapter and:
- authenticates per token (Bearer) and returns plain string-keyed Hashes
(get/post) or raw bodies (raw_get) -- one response shape, no
Octokit/Sawyer
- only ever sends the access token to api.github.com and
raw.githubusercontent.com, rejecting any other absolute URL, so a
user-derived path can never leak a token to an arbitrary host
- backs off on rate limits both reactively (403/429) and proactively
(when
X-RateLimit-Remaining hits 0), honouring Retry-After /
X-RateLimit-Reset,
via a shared Redis flag (GithubRateLimit) keyed per token so each
token's
budget and the shared unauthenticated/IP budget back off independently
- short-circuits while backing off without ever sleeping, so onebox
rendering
and post baking degrade to a plain link instead of blocking a request
- caches ETags and sends If-None-Match, so unchanged resources return
304s
that do not count against the rate limit
Every caller was moved onto it:
- the 6 core GitHub onebox engines, via a slimmed
Onebox::Mixins::GithubApi
adapter that keeps their public methods and translates client errors
back
to the OpenURI::HTTPError vocabulary they already rescue (engines
unchanged)
- the github_blob raw.githubusercontent.com fetch
- the discourse-github plugin (badges, linkback, permalinks, token
validator),
which no longer uses the octokit and sawyer gems (they stay in the
Gemfile for
the discourse-code-review official plugin, which still depends on them)
- the discourse-ai bot's GitHub tools (search code, diff, file content,
search files)
Also adds a GithubOneboxBackoff admin problem check that surfaces while
one of
the onebox token identities is backing off -- scoped to the tokens
resolved by
Onebox::GithubAccess (each configured github_onebox_access_tokens entry
plus the
unauthenticated client) so a backoff on the AI bot or linkback token is
not
misattributed to onebox. Its message points admins at the relevant
setting with
the {{setting:...}} link marker, which problem-check messages now expand
too.
Onebox token resolution is centralised in Onebox::GithubAccess, and the
onebox
cache TTL for transient GitHub failures is shortened so they recover
quickly.
GitHub OAuth login, theme git-clone, the inbound webhook, and the
Oneboxer
FinalDestination URL-resolution special-cases for github.com are
intentionally
out of scope -- they are different concerns, not the rate-limited data
API.
Previously, the workflow Template node rendered Mustache template. This
change uses Liquid instead as we think it's a better UX. Shopify uses
(and created) liquid making it a very well know system.
Previously, the migrations tooling was a single flat `migrations/` tree,
autoloaded by one global Zeitwerk loader and driven by a Thor CLI, so each
planned next step had nowhere clean to land.
This change splits it into four `path:`-referenced gems — `migrations-core`,
`migrations-tooling`, `migrations-converters`, and `migrations-importer` —
served by a single Samovar-based `disco` binary, without rewriting any domain
logic.
### Why now
The DSL refactor that replaced the IntermediateDB YAML config just landed,
which is the cheapest moment to do this. Everything queued behind it — column
coverage verification, the `discourse-migrations` validation plugin, the
transformer framework, and private converter isolation — either has nowhere
clean to land in the flat tree or would have to be retrofitted into a gem
layout later. Doing the split now, while it's still a pure move (suite green,
no domain logic touched), is far cheaper than after another round of features
has built on the flat layout.
### What changes
- **Four gems under `migrations/`**, all `path:`-referenced from the root
`Gemfile` (nothing is published to RubyGems): `core` (CLI framework, UI, DB
infrastructure, IntermediateDB, and the conversion framework), `tooling`
(schema DSL and `schema` commands), `converters` (implementations and source
adapters), and `importer` (row and uploads import).
- **A single CLI binary:** `migrations/bin/cli` (Thor) becomes `disco`
(Samovar), with each gem registering its own commands. Same surface —
`convert`, `import`, `upload`, `schema generate|validate|…` — and Rails is
still booted lazily.
- **Isolated test suites:** each gem runs its own no-Rails specs in a new CI
job, while the existing job keeps running the Rails-integration specs.
Document attachments (doc, docx, xls, xlsx, rtf, csv, md, txt) are now
converted to text before being included in LLM prompts, instead of
being forwarded as raw base64 payloads. PDFs remain the only format
sent as a raw upload, capped at 10MB.
New converters under lib/completions:
- DocToText shells out to antiword
- DocxToText parses OOXML directly with size and depth limits
- XlsToText shells out to xls2csv
- XlsxToText parses OOXML and shared strings into CSV-style text
- RtfToText is a custom RTF tokenizer with destination/group handling
Plain text formats (csv, md, txt) are read with a 1MB byte cap and
UTF-8 normalization. Extracted text is truncated to 100k characters,
with a preamble noting the original filename and size.
Dialect trimming now uses token-aware truncation against a per-message
budget so large extracted documents collapse cleanly under the prompt
limit, rather than the previous step-based slicing of raw content.
Other changes:
- LlmModel.normalize_attachment_types is shared with UploadEncoder and
collapses "markdown" to "md" so the canonical extension is consistent
across model config, UI defaults, and encoder output
- ai-llm-attachment-types adds csv, xls, xlsx to the default choices
- Locale strings clarify that vision controls images and
allowed_attachment_types controls documents
---------
Co-authored-by: Rafael Silva <xfalcox@gmail.com>
This commit adds support for assuming an IAM role when performing S3
operations. When `s3_role_arn` is configured alongside static access
keys, Discourse will use AWS STS AssumeRole to obtain temporary, scoped
credentials instead of using the static keys directly.
Two new settings are introduced:
1. `s3_role_arn`: The ARN of the IAM role to assume.
2. `s3_role_session_name`: An optional session name which falls back to
the server hostname when blank.
Both are available as GlobalSettings (env vars) and SiteSettings (admin
UI). The feature is entirely opt-in, when `s3_role_arn` is blank
everything behaves exactly as before.
Co-authored-by: ducks <868959+ducks@users.noreply.github.com>
Pitchfork has been the default web server for some time now. This
removes Unicorn entirely to simplify the codebase and unblock future
improvements (like Rack 3).
Notable changes beyond the straightforward removal:
- `Discourse.after_unicorn_worker_fork` →
`Discourse.apply_worker_db_variables_overrides`: renamed and wired into
pitchfork.conf.rb's `after_worker_fork`. This actually *fixes*
per-worker DB variable overrides (`unicorn_worker_db_variables_*`) which
were never called under Pitchfork.
- `bin/ember-cli`: `--unicorn` flag renamed to `--server` (`-u` kept).
- `lib/demon/sidekiq.rb`: removed Unicorn-specific USR1/USR2 signal
handlers and `reopen_logs` (called `Unicorn::Util.reopen_logs`), which
were already dead code under Pitchfork.
Intentionally kept unchanged:
- `config/unicorn_launcher` (used by Docker images, separate effort)
- `docker_manager` plugin (separate repo)
- `UNICORN_*` env vars (renaming deferred)
- Rack < 3 constraint (separate PR)
## Summary
Adds a new `aws_bedrock_converse` inference provider that uses the
official AWS SDK (`aws-sdk-bedrockruntime`) and the Converse API. This
runs alongside the existing `aws_bedrock` provider — fully additive,
zero risk to existing configurations.
### Why a new provider?
The existing `aws_bedrock` provider manually handles SigV4 signing, URL
construction, binary event stream decoding, and maintains a hardcoded
model ID mapping table. It only supports Claude and Nova models.
The new provider delegates all of this to the official AWS SDK, which
means:
- **Model-agnostic** — works with any model available on Bedrock
(Claude, Nova, Kimi, MiniMax, Mistral, Llama, DeepSeek, NVIDIA, Qwen,
GLM, etc.) without any model-specific code
- **Application Inference Profiles** — users can set cross-region
profiles (`us.anthropic.claude-sonnet-4-20250514-v1:0`) or application
inference profile ARNs directly as the model name
- **Bedrock API Key auth** — supports the new AWS Bedrock API keys
(Bearer token auth) in addition to IAM access keys, STS role assumption,
and automatic credential resolution from environment/instance profiles
- **No maintenance burden** — no model ID mapping table to update when
AWS adds new models, no manual SigV4 signing, no binary event stream
decoding
- **Native tools only** — no XML tool fallback; uses the Converse API's
built-in tool support
### Authentication options (priority order)
| Config | Auth method |
|---|---|
| `role_arn` set | STS AssumeRole (SigV4) |
| `access_key_id` set | Static IAM credentials (SigV4) |
| API key set (no access_key_id/role_arn) | Bearer token (Bedrock API
key) |
| Nothing set | SDK auto-resolves (env vars, instance profile, ECS task
role) |
### Features supported
- Streaming and non-streaming completions
- Native tool use with tool_choice (auto/any/specific tool)
- Structured output via Converse API's `output_config` (models that
support it)
- Extended thinking / adaptive thinking with signature preservation for
multi-turn
- Interleaved thinking with tool calls (thinking blocks preserved per
tool_call message)
- Prompt caching via `cache_point` blocks
- Effort parameter (low/medium/high/max)
- `extra_model_fields` provider param for arbitrary
`additionalModelRequestFields` (beta features like `anthropic_beta`, 1M
context, interleaved thinking)
### New files
- `lib/completions/endpoints/aws_bedrock_converse.rb` — endpoint using
`Aws::BedrockRuntime::Client`
- `lib/completions/dialects/converse.rb` — unified Converse API dialect
- `lib/completions/dialects/converse_tools.rb` — tool formatting
- `lib/completions/converse_message_processor.rb` — response processing
for SDK typed objects
## Tested against real Bedrock API
All tests performed using Bedrock API Key auth (Bearer token) against
live endpoints with 9 different models from 8 providers:
| Test | Claude Sonnet 4 | Claude Haiku 4.5 | Kimi K2.5 | MiniMax M2 |
DeepSeek 3.2 | NVIDIA Nemotron 3 120B | Qwen3 Next 80B | GLM 5 | Mistral
Small |
|---|---|---|---|---|---|---|---|---|---|
| Non-streaming text | ✅ | ✅ |
✅ | ✅ | ✅ |
✅ | ✅ | ✅ |
✅ |
| Streaming text | ✅ | ✅ |
✅ | ✅ | ✅ |
✅ | ✅ | ✅ |
✅ |
| Multi-turn conversation | ✅ | ✅ |
✅ | ✅ | ✅ |
✅ | ✅ | ✅ |
✅ |
| Tool use (non-streaming) | ✅ | ✅ |
✅ | ✅ | ✅ |
✅ | ✅ | ✅ |
✅ |
| Tool use (streaming) | ✅ | ✅ |
✅ | ✅ | ✅ |
✅ | ✅ | ✅ | ❌ model
unsupported |
| Structured output (non-streaming) | — | ✅ | ❌ model
unsupported | ✅ | ✅ |
✅ | ✅ | ✅ | ❌ model
unsupported |
| Structured output (streaming) | — | ✅ | ❌ model
unsupported | ✅ | ✅ |
✅ | ✅ | ✅ | ❌ model
unsupported |
| Bearer token auth | ✅ | ✅ |
✅ | ✅ | ✅ |
✅ | ✅ | ✅ |
✅ |
| Cross-region inference profile | ✅ |
✅ | — | — | — | — | — | — | — |
| Audit logging + token tracking | ✅ |
✅ | ✅ | ✅ |
✅ | ✅ | ✅ |
✅ | ✅ |
> **Notes:**
> - Claude Sonnet 4 structured output not tested — requires 4.5+ for
this feature and those cross-region profiles were not available in the
test region.
> - Kimi K2.5 and Mistral Small do not support Bedrock's native
structured output.
> - Mistral Small does not support streaming tool use.
> - All ❌ results are model-level limitations, not code issues — the
Converse API correctly surfaces the error.
## Test plan
- [ ] Existing `aws_bedrock` provider tests pass (`bin/rspec
spec/lib/completions/endpoints/aws_bedrock_spec.rb`)
- [ ] New provider tests pass (`bin/rspec
spec/lib/completions/endpoints/aws_bedrock_converse_spec.rb`)
- [ ] Create an LLM model with provider "AWS Bedrock (Converse API)" in
admin UI
- [ ] Verify basic completion works with a Bedrock API key (just region
+ API key, no IAM keys needed)
- [ ] Verify tool use works in AI bot conversations
- [ ] Verify structured output works with a supported model (Claude
Haiku 4.5+)
Puma is no longer used as an application server in production or
development — Pitchfork is the default, invoked automatically via
`bin/unicorn`. The only remaining use of Puma is as Capybara's embedded
test server, where it runs in a single-process threaded mode to share
database transactions with the test suite.
This commit moves the `puma` gem from a top-level dependency into the
test group to reflect its actual usage. The production Puma
configuration file is removed since it is unused, and stale
`defined?(Puma)` checks and comments are cleaned up.
- Moves it from `version_bump.rake` to the new `release.rake`. This is
the last thing which was pending upgrade, so we can now delete the whole
`version_bump.rake` file & spec
- Adds support for release/* branches
- Adds an interactive prompt to choose which security PRs to include
- Creates & merges the PR using `gh` CLI. Less manual work.
We ran into trouble with MethodProfiler referencing excon without having loaded
it as it was relying on an initialiser to load it and depending on the side
effect.
If the excon gem is going to be loaded anyways, it doesn't make sense to have
it not loaded by default; this will be more robust.
per
https://meta.discourse.org/t/imap-support-for-group-inboxes/160588/39?u=martin
we have been planning to remove IMAP support for a while,
because of its low usage and adoption, high complexity, and maintenance
burden.
This commit removes all IMAP-related code, including models,
jobs, services, and frontend components.
---------
Co-authored-by: Régis Hanol <regis@hanol.fr>
This upgrades to Mathjax 4.1 and latest katex
Implement rich text composer support for math
Adds support for /( )/ and /[ /] which was missing and very common now
Removes math javascript from our repo
---------
Co-authored-by: Mark McClure <mcmcclur@unca.edu>
This PR adds Pitchfork, as we want to move away from Unicorn ultimately.
Unicorn still boots by default, so there should be no disruption for
anyone.
To use Pitchfork instead of Unicorn, the `RUN_PITCHFORK` environment
variable must be set.
This will make `bin/rails s` and `config/unicorn_launcher` boot
Pitchfork. `unicorn_launcher` was patched because that way we can easily
switch between Unicorn and Pitchfork without having to change too many
things on the infra side.
The upgrader from the `docker_manager` plugin doesn’t work yet with
Pitchfork. This will be addressed in a future PR.
Recently we replaced the `pry-byebug` gem by the `debug` one. It broke
the auto-reload mechanism we have in the development environment for
Unicorn.
Indeed, the `debug` gem by default will add an `at_exit` hook that will
wait for all its children to exit. It clashes with our own mechanism.
The workaround is to require `debug/prelude` instead of `debug`: the
`debugger` command and breakpoints will still work but without the hook
being set.
The official `debug` gem provides more modern debugging capabilities,
better syntax highlighting, the native ability to use vscode / chrome
debuggers and is maintained alongside ruby itself.
This commit is a complete reimplementation of our theme JS compilation
system.
Previously, we compiled theme JS into AMD `define` statements on a
per-source-file basis, and then concatenated them together for the
client. These AMD modules would integrate with those in Discourse core,
allowing two way access between core/theme modules. Going forward, we'll
be moving away from AMD, and towards native ES modules in core. Before
we can do that, we need to stop relying on AMD as the 'glue' between
core and themes/plugins.
This change introduces Rollup (running in mini-racer) as a compiler for
theme JS. This is configured to generate a single ES Module which
exports a list of 'compat modules'. Core `import()`s the modules for
each active theme, and adds them all to AMD. In future, this consumption
can be updated to avoid AMD entirely.
All module resolution within a theme is handled by Rollup, and does not
use AMD.
Import of core/plugin modules from themes are automatically transformed
into calls to a new `window.moduleBroker` interface. For now, this is a
direct interface to AMD. In future, this can be updated to point to real
ES Modules in core.
Despite the complete overhaul of the internals, this is not a breaking
change, and should have no impact on existing themes. If any
incompatibilities are found, please report them on
https://meta.discourse.org.
---------
Co-authored-by: Jarek Radosz <jarek@cvx.dev>
Co-authored-by: Chris Manson <chris@manson.ie>
When enabled this will convert uploaded videos to a standard format that should
be playable on all devices and browsers.
The goal of this feature is to prevent codec playback issues that
sometimes can occur with video uploads.
It uses an adapter pattern, so that other services for video conversion
could be easily added in the future.
- Migrated from annotate to annotaterb as the former is not maintained
anymore.
- Dropped our `fast_pluck` patch as the default `pluck` implementation
seems now faster.
A few minor versions of Bullet were incompatible with Discourse because we use our own content security policy middleware.
This has now been fixed upstream and released in 8.0.7.
Because we use a custom ContentSecurityPolicy middleware, the latest versions (> 8.0.3) of Bullet error out on load.
This PR locks the gem to the latest compatible version.
This commit is replacing the system specs driver (selenium) by
Playwright: https://playwright.dev/
We are still using Capybara to write the specs but they will now be run
by Playwright. To achieve this we are using the non official ruby
driver: https://github.com/YusukeIwaki/capybara-playwright-driver
### Notable changes
- `CHROME_DEV_TOOLS` has been removed, it's not working well with
playwright use `pause_test` and inspect browser for now.
- `fill_in` is not generating key events in playwright, use `send_keys`
if you need this.
### New spec options
#### trace
Allows to capture a trace in a zip file which you can load at
https://trace.playwright.dev or locally through `npx playwright
show-trace /path/to/trace.zip`
_Example usage:_
```ruby
it "shows bar", trace: true do
visit("/")
find(".foo").click
expect(page).to have_css(".bar")
end
```
#### video
Allows to capture a video of your spec.
_Example usage:_
```ruby
it "shows bar", video: true do
visit("/")
find(".foo").click
expect(page).to have_css(".bar")
end
```
### New env variable
#### PLAYWRIGHT_SLOW_MO_MS
Allow to force playwright to wait DURATION (in ms) at each action.
_Example usage:_
```
PLAYWRIGHT_SLOW_MO_MS=1000 rspec foo_spec.rb
```
#### PLAYWRIGHT_HEADLESS
Allow to be in headless mode or not. Default will be headless.
_Example usage:_
```
PLAYWRIGHT_HEADLESS=0 rspec foo_spec.rb # will show the browser
```
### New helpers
#### with_logs
Allows to access the browser logs and check if something specific has
been logged.
_Example usage:_
```ruby
with_logs do |logger|
# do something
expect(logger.logs.map { |log| log[:message] }).to include("foo")
end
```
#### add_cookie
Allows to add a cookie on the browser session.
_Example usage:_
```ruby
add_cookie(name: "destination_url", value: "/new")
```
#### get_style
Get the property style value of an element.
_Example usage:_
```ruby
expect(get_style(find(".foo"), "height")).to eq("200px")
```
#### get_rgb_color
Get the rgb color of an element.
_Example usage:_
```ruby
expect(get_rgb_color(find("html"), "backgroundColor")).to eq("rgb(170, 51, 159)")
```
We are no longer using any of the transpilation/bundling features of
Sprockets. We only use it to serve assets in development, and then
collect & fingerprint them in production. This commit switches us to use
the more modern "Propshaft" gem for that functionality.
Propshaft is much simpler than Sprockets. Instead of taking a
combination of paths + "precompile" list, Propshaft simply assumes all
files in the configured directory are required in production. Previously
we had some base paths configured quite high in the directory structure,
and then only precompiled selected assets within the directory. That's
no longer possible, so this commit refactors those places (mostly
plugin-related) to use dedicated directories under
`app/assets/generated/`.
Another difference is that Propshaft applies asset digests in
development as well as production. This is great for caching & dev/prod
consistency, but does mean some small changes were required in tests.
We previously had some freedom-patches applied to Sprockets. Some of
those had to be ported across to Propshaft. We now have three patches:
1. Skip adding digest hashes to webpack-generated chunks (which are
already digested, and referred to from other js files)
2. Avoid raising errors for missing assets in test mode. We don't always
compile assets before running basic RSpec tests.
3. Maintain relative paths for sourcemap URLs, so that files don't need
to be recompiled depending on their CDN path
Significant refactors are made to the `assets.rake` and `s3.rake` tasks,
which rely on implementation details of Sprockets/Propshaft.
Previously all locale bundles would be built & compressed during
assets:precompile. For most sites, only one of these languages was
actually used, so this is fairly wasteful.
This commit moves the main locale bundle into the
ExtraLocalesController, which has recently undergone many improvements
to make it more efficient. This allows locale files to be bundled "just
in time" when they're first accessed.
Now that brotli level=6 is enabled for these assets in our nginx config,
this change should have no impact on the locale bundle size.
Only notable changes is that we added
internal support for TimeWithZone which
was absent from previous release
We also improved error messages for T_OBJECT