mirror of
https://github.com/discourse/discourse.git
synced 2026-08-08 17:53:55 +08:00
This PR adds a `CAPYBARA_PLAYWRIGHT_SOFT_RESET` env flag which reuses the Playwright browser context between system specs so each example no longer pays the full bundle load cost. `capybara-playwright-driver` already keeps the browser process alive, but its reset closes the context after every example. That throws away the context-level HTTP cache and other browser work cached with the context, so the next example pays the bundle load cost again. Key changes: * Add `PlaywrightSoftReset` in `spec/support/system/capybara_patches.rb` so `Capybara::Playwright::Driver#reset!` clears storage, cookies, and permissions instead of closing the context. This keeps per-example isolation while leaving the browser caches warm. * Detect installed fake clocks by probing `setTimeout.toString()` on the fresh page during the soft reset. Playwright's clock is context-scoped and cannot be uninstalled, so any example that installed one (via `BrowserTime.freeze` or `pw_page.clock.install` directly) automatically gets a full context reset without having to declare anything. * Detect downloads by listening for the context's `download` event. Contexts that produced downloads fall back to a full reset so Playwright's own artifact lifecycle handles cleanup, avoiding races with reused contexts. * Clear `ExtraLocalesController`'s bundle digest cache in `TestSetup` whenever leftover translation overrides are detected (the same branch that already calls `I18n.reload!`). Overrides change locale bundle contents but specs that create them with `create!`/`update_columns` bypass the digest invalidation that `TranslationOverride.upsert!` performs, so a warm HTTP cache could serve stale bundles. A fresh digest changes the bundle URL, which bypasses every cache naturally. * Store downloads under a worker-local `Downloads::FOLDER` so per-worker cleanup cannot delete another parallel worker's artifacts. * Block service workers in system specs (`serviceWorkers: "block"`). Letting each example register a service worker that the soft reset then force-clears caused mass spec timeouts on CI. The exact cause requires further investigation, but system specs currently do not rely on service workers so blocking them is an acceptable trade-off. * Measure the impact on a 16-vCPU DO droplet over three rounds, where the core system suite dropped from ~843s to ~721s (~14.5%). The optimization is opt-in via `CAPYBARA_PLAYWRIGHT_SOFT_RESET=1`, which CI sets only for the core system and chat plugin system jobs while the optimization proves itself.
87 lines
3 KiB
Ruby
Vendored
87 lines
3 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
# Per-test global-state reset, run before every example and every before_all
|
|
# block. `before_next_spec` queues one-shot cleanups that TestSetup drains here.
|
|
|
|
module TestSetup
|
|
# This is run before each test and before each before_all block
|
|
def self.test_setup(x = nil)
|
|
# This allows DB.transaction_open? to work in tests. See lib/mini_sql_multisite_connection.rb
|
|
DB.test_transaction = ActiveRecord::Base.connection.current_transaction
|
|
|
|
RateLimiter.disable
|
|
PostActionNotifier.disable
|
|
SearchIndexer.disable
|
|
UserActionManager.disable
|
|
NotificationEmailer.disable
|
|
SiteIconManager.disable
|
|
WordWatcher.disable_cache
|
|
UpcomingChanges.clear_caches!
|
|
|
|
SiteSetting.provider.all.each { |setting| SiteSetting.remove_override!(setting.name) }
|
|
|
|
# Set some standard overrides for tests. Some for performance, some to make the tests easier,
|
|
# and some because their default was changed, and we didn't want to refactor all the relevant specs.
|
|
{
|
|
s3_upload_bucket: "bucket",
|
|
min_post_length: 5,
|
|
min_first_post_length: 5,
|
|
min_personal_message_post_length: 10,
|
|
download_remote_images_to_local: false,
|
|
unique_posts_mins: 0,
|
|
max_consecutive_replies: 0,
|
|
allow_uncategorized_topics: true,
|
|
}.each { |k, v| SiteSetting.set(k, v) }
|
|
|
|
SiteSetting.refresh!(refresh_site_settings: false, refresh_theme_site_settings: true)
|
|
SiteSetting.refresh_site_setting_group_ids!
|
|
|
|
# very expensive IO operations
|
|
SiteSetting.automatically_download_gravatars = false
|
|
|
|
Discourse.clear_readonly!
|
|
Sidekiq::Worker.clear_all
|
|
|
|
I18n.locale = SiteSettings::DefaultsProvider::DEFAULT_LOCALE
|
|
|
|
# Database is rolled back between specs, but I18n override cache doesn't.
|
|
# Flush it if there were any TranslationOverrides created.
|
|
overrides_by_site = I18n.instance_variable_get(:@overrides_by_site) || {}
|
|
if overrides_by_site.values.flat_map(&:values).any?(&:any?)
|
|
I18n.reload!
|
|
ExtraLocalesController.clear_cache!
|
|
end
|
|
|
|
RspecErrorTracker.clear_exceptions
|
|
|
|
if $test_cleanup_callbacks
|
|
$test_cleanup_callbacks.reverse_each(&:call)
|
|
$test_cleanup_callbacks = nil
|
|
end
|
|
|
|
# in test this is very expensive, we explicitly enable when needed
|
|
Topic.update_featured_topics = false
|
|
|
|
# Running jobs are expensive and most of our tests are not concern with
|
|
# code that runs inside jobs. run_later! means they are put on the redis
|
|
# queue and never processed.
|
|
Jobs.run_later!
|
|
|
|
# Don't track ApplicationRequests in test mode unless opted in
|
|
ApplicationRequest.disable
|
|
|
|
# Don't queue badge grant in test mode
|
|
BadgeGranter.disable_queue
|
|
|
|
OmniAuth.config.test_mode = false
|
|
|
|
Middleware::AnonymousCache.disable_anon_cache
|
|
BlockRequestsMiddleware.allow_requests!
|
|
BlockRequestsMiddleware.current_example_location = nil
|
|
ApplicationSerializer.fragment_cache.clear
|
|
end
|
|
end
|
|
|
|
def before_next_spec(&callback)
|
|
($test_cleanup_callbacks ||= []) << callback
|
|
end
|