diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml
index 2c78f7cf..d193ace5 100644
--- a/.github/workflows/ci_tests.yml
+++ b/.github/workflows/ci_tests.yml
@@ -48,22 +48,10 @@ jobs:
node-version-file: .nvmrc
cache: npm
- - name: Prepare database
- run: bundle exec rails db:schema:load
-
- - name: Install node dependencies
- run: npm ci
-
- - name: Precompile assets
- run: bundle exec rake assets:precompile
-
- - name: Run unit tests
- run: bundle exec rails test
-
- name: Set up Chrome
uses: browser-actions/setup-chrome@latest
with:
chrome-version: stable
- - name: Run system tests
- run: HEADLESS=1 bundle exec rails test:system
+ - name: Run CI
+ run: bin/ci
diff --git a/.prettierignore b/.prettierignore
index 7a5515d4..1a9df9d6 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -1,6 +1,8 @@
.rbenv-vars
*.rb
*.rake
+.context/
app/assets/builds/
public/vite*/
tmp/
+vendor/
diff --git a/app/controllers/user/views_controller.rb b/app/controllers/user/views_controller.rb
deleted file mode 100644
index 7f18d99b..00000000
--- a/app/controllers/user/views_controller.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-# frozen_string_literal: true
-
-class User::ViewsController < User::ApplicationController
- def index
- metric = Metric.find(params[:metric_id])
- render json: metric.views
- end
-
- def create
- metric = current_user.metrics.find(params[:metric_id])
- current_user.views.create!(viewable: metric)
- head :created
- end
-end
diff --git a/app/javascript/components/App.tsx b/app/javascript/components/App.tsx
index 83f95fe6..b547b906 100644
--- a/app/javascript/components/App.tsx
+++ b/app/javascript/components/App.tsx
@@ -62,18 +62,18 @@ const App = ({ alert, notice }: { alert: string; notice: string }) => {
-
+
)
}
diff --git a/app/javascript/components/Breadcrumb.tsx b/app/javascript/components/Breadcrumb.tsx
index 140dcab3..c87c387e 100644
--- a/app/javascript/components/Breadcrumb.tsx
+++ b/app/javascript/components/Breadcrumb.tsx
@@ -2,12 +2,10 @@ import { Avatar, Breadcrumb as BaseBreadcrumb, Button, Dropdown, Tooltip } from
import { useNavigate, useSearchParams } from 'react-router-dom'
import { useMetricWatchersCreate, useMetricWatchersDestroy } from '../queries/user/metricWatchers'
-import _ from 'lodash'
import useCurrentUser from '../hooks/useCurrentUser'
import { Metric } from '../queries/user/metrics'
import { Project } from '../queries/user/projects'
import { useUsersIndex } from '../queries/user/users'
-import { useViewsIndex } from '../queries/user/views'
import MetricActionsMenu from './MetricActionsMenu'
import ProjectActionsMenu from './ProjectActionsMenu'
@@ -25,10 +23,6 @@ const Breadcrumb = ({ projects, metrics }: { projects: Project[]; metrics: Metri
const metricId = parseInt(searchParams.get('metric_id') || '')
const currentMetric = metricId ? metrics.find((metric) => metric.id === metricId) : null
- const { data: views } = useViewsIndex({ metricId })
- const viewerIds = views ? _.uniq(views.map((view) => view.user_id)) : []
- const { data: viewers } = useUsersIndex({ ids: viewerIds, enabled: !!currentMetric })
-
const { data: watchers } = useUsersIndex({ ids: currentMetric?.watcher_ids, enabled: !!currentMetric })
if (!user) return null
@@ -95,24 +89,6 @@ const Breadcrumb = ({ projects, metrics }: { projects: Project[]; metrics: Metri
{currentProject && (
{!currentMetric &&
}
- {viewers && views && views.length > 0 && (
- <>
-
Seen by:
-
- {viewers.map((viewer) => (
-
- navigate(`/user/users/${viewer.id}`)}
- />
-
- ))}
-
- >
- )}
{currentMetric &&
}
)}
diff --git a/app/javascript/components/MetricCard.tsx b/app/javascript/components/MetricCard.tsx
index 03b886f0..33b21ce1 100644
--- a/app/javascript/components/MetricCard.tsx
+++ b/app/javascript/components/MetricCard.tsx
@@ -1,20 +1,10 @@
import { Card } from 'flowbite-react'
-import { useEffect } from 'react'
import { ChartKind } from '../queries/user/charts'
-import { useViewsCreate } from '../queries/user/views'
import MetricChart from './MetricChart'
-const MetricCard = ({ metricId, owners }: { metricId: number; owners?: string[] }) => {
- const { mutate: trackView } = useViewsCreate()
-
- useEffect(() => {
- trackView(metricId)
- }, [metricId, trackView])
-
- return (
-
-
-
- )
-}
+const MetricCard = ({ metricId, owners }: { metricId: number; owners?: string[] }) => (
+
+
+
+)
export default MetricCard
diff --git a/app/javascript/queries/user/views.ts b/app/javascript/queries/user/views.ts
deleted file mode 100644
index 669df985..00000000
--- a/app/javascript/queries/user/views.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { useMutation, useQuery } from '@tanstack/react-query'
-import axios from 'axios'
-
-type View = {
- created_at: string
- id: number
- updated_at: string
- user_id: number
- viewable_id: number
- viewable_type: string
-}
-
-export const useViewsIndex = ({ metricId }: { metricId: number }) =>
- useQuery(
- ['user', 'views', { metricId }],
- () =>
- axios
- .get(`/user/views.json`, { params: { metric_id: metricId } })
- .then((response) => response.data),
- {
- enabled: !!metricId,
- }
- )
-
-export const useViewsCreate = () =>
- useMutation((metricId: number) => axios.post(`/user/views.json`, { metric_id: metricId }))
diff --git a/app/models/metric.rb b/app/models/metric.rb
index f438c44c..733eedb1 100644
--- a/app/models/metric.rb
+++ b/app/models/metric.rb
@@ -7,7 +7,6 @@ class Metric < ApplicationRecord
has_many :reports, dependent: :destroy
has_many :contributions, dependent: :destroy
has_many :chart_metrics, dependent: :destroy
- has_many :views, as: :viewable, dependent: :destroy
validates :name, presence: true
diff --git a/app/models/user.rb b/app/models/user.rb
index 47b675b1..3ae15b66 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -12,7 +12,6 @@ class User < ApplicationRecord
has_many :metrics, through: :projects
has_many :notifications, dependent: :destroy
has_many :owned_organizations, class_name: Organization.to_s, dependent: :restrict_with_error
- has_many :views, dependent: :destroy
before_save :ensure_api_key
diff --git a/app/models/view.rb b/app/models/view.rb
deleted file mode 100644
index 1f3de03d..00000000
--- a/app/models/view.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-# frozen_string_literal: true
-
-class View < ApplicationRecord
- belongs_to :user
- belongs_to :viewable, polymorphic: true
-end
diff --git a/bin/ci b/bin/ci
index 406c5a94..f95dd364 100755
--- a/bin/ci
+++ b/bin/ci
@@ -30,9 +30,14 @@ else
step "Setup: Dependencies" bundle install
step "Setup: Dependencies" npm install
fi
-# Kill stale connections to cherry_test so db:test:prepare can purge it
-psql -d cherry_development -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'cherry_test' AND pid <> pg_backend_pid()" > /dev/null 2>&1 || true
-step "Setup: Database" bin/rails db:test:prepare
+if [ "${CI:-}" = "true" ]; then
+ # Fresh CI database — no need to purge, just create and load schema.
+ step "Setup: Database" bin/rails db:create db:schema:load
+else
+ # Kill stale connections to cherry_test so db:test:prepare can purge it.
+ psql -d cherry_development -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'cherry_test' AND pid <> pg_backend_pid()" > /dev/null 2>&1 || true
+ step "Setup: Database" bin/rails db:test:prepare
+fi
# Linting
[ $FAILED -eq 0 ] && step "Style: Ruby" bundle exec rubocop
diff --git a/config/routes.rb b/config/routes.rb
index 76cf67d0..c6c9003d 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -42,7 +42,6 @@
resource :settings, only: %i[update]
resources :subscriptions, only: %i[create]
resources :users, only: %i[index show]
- resources :views, only: %i[index create]
end
constraints(->(request) { request.format == :html }) do
diff --git a/db/migrate/20260417172507_drop_views.rb b/db/migrate/20260417172507_drop_views.rb
new file mode 100644
index 00000000..644c792f
--- /dev/null
+++ b/db/migrate/20260417172507_drop_views.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class DropViews < ActiveRecord::Migration[7.1]
+ def change
+ drop_table :views do |t|
+ t.references :user, null: false, foreign_key: true
+ t.references :viewable, polymorphic: true, null: false
+ t.timestamps
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 6408024a..9e18b8ba 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.1].define(version: 2026_04_14_163240) do
+ActiveRecord::Schema[7.2].define(version: 2026_04_17_172507) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_stat_statements"
enable_extension "plpgsql"
@@ -222,16 +222,6 @@
t.integer "favorite_dashboard_ids", default: [], array: true
end
- create_table "views", force: :cascade do |t|
- t.bigint "user_id", null: false
- t.string "viewable_type", null: false
- t.bigint "viewable_id", null: false
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
- t.index ["user_id"], name: "index_views_on_user_id"
- t.index ["viewable_type", "viewable_id"], name: "index_views_on_viewable"
- end
-
add_foreign_key "authorization_requests", "organizations"
add_foreign_key "authorization_requests", "users"
add_foreign_key "authorizations", "organizations"
@@ -247,5 +237,4 @@
add_foreign_key "projects", "organizations"
add_foreign_key "projects", "users"
add_foreign_key "reports", "metrics"
- add_foreign_key "views", "users"
end
diff --git a/package-lock.json b/package-lock.json
index fab068e0..857dfd65 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -60,6 +60,7 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
+ "peer": true,
"engines": {
"node": ">=10"
},
@@ -330,7 +331,6 @@
"version": "11.11.1",
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.1.tgz",
"integrity": "sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==",
- "peer": true,
"dependencies": {
"@babel/runtime": "^7.18.3",
"@emotion/babel-plugin": "^11.11.0",
@@ -376,7 +376,6 @@
"version": "11.13.0",
"resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.13.0.tgz",
"integrity": "sha512-tkzkY7nQhW/zC4hztlwucpT8QEZ6eUzpXDRhww/Eej4tFfO0FxQYWRyg/c5CCXa4d/f174kqeXYjuQRnhzf6dA==",
- "peer": true,
"dependencies": {
"@babel/runtime": "^7.18.3",
"@emotion/babel-plugin": "^11.12.0",
@@ -1039,6 +1038,7 @@
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
"integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
+ "peer": true,
"dependencies": {
"@jridgewell/set-array": "^1.0.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
@@ -1052,6 +1052,7 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
"integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
+ "peer": true,
"engines": {
"node": ">=6.0.0"
}
@@ -1060,6 +1061,7 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+ "peer": true,
"engines": {
"node": ">=6.0.0"
}
@@ -1067,12 +1069,14 @@
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.15",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
- "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
+ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
+ "peer": true
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.20",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz",
"integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==",
+ "peer": true,
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
@@ -1138,7 +1142,6 @@
"version": "5.16.7",
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.16.7.tgz",
"integrity": "sha512-cwwVQxBhK60OIOqZOVLFt55t01zmarKJiJUWbk0+8s/Ix5IaUzAShqlJchxsIQ4mSrWqgcKCCXKtIlG5H+/Jmg==",
- "peer": true,
"dependencies": {
"@babel/runtime": "^7.23.9",
"@mui/core-downloads-tracker": "^5.16.7",
@@ -1363,7 +1366,6 @@
"resolved": "https://registry.npmjs.org/@prettier/plugin-ruby/-/plugin-ruby-1.6.1.tgz",
"integrity": "sha512-PGDCATgVTQz0s/NB9nStiXVCIr+hG/XnKeAO/kguaHrNf8VwCpP5Ul+/KQao0z0QFBy2PDY8kWptfDQCa7WMXg==",
"dev": true,
- "peer": true,
"dependencies": {
"prettier": ">=1.10"
}
@@ -1800,7 +1802,6 @@
"version": "4.36.1",
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.36.1.tgz",
"integrity": "sha512-y7ySVHFyyQblPl3J3eQBWpXZkliroki3ARnBKsdJchlgt7yJLRDUcf4B8soufgiYt3pEQIkBWBx1N9/ZPIeUWw==",
- "peer": true,
"dependencies": {
"@tanstack/query-core": "4.36.1",
"use-sync-external-store": "^1.2.0"
@@ -1936,7 +1937,6 @@
"version": "18.2.42",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.42.tgz",
"integrity": "sha512-c1zEr96MjakLYus/wPnuWDo1/zErfdU9rNsIGmE+NV71nx88FG9Ttgo5dqorXTu/LImX2f63WBP986gJkMPNbA==",
- "peer": true,
"dependencies": {
"@types/prop-types": "*",
"@types/scheduler": "*",
@@ -2254,7 +2254,6 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz",
"integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==",
"dev": true,
- "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -2315,12 +2314,14 @@
"node_modules/any-promise": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
- "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="
+ "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
+ "peer": true
},
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "peer": true,
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
@@ -2333,7 +2334,6 @@
"version": "3.49.1",
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.49.1.tgz",
"integrity": "sha512-MqGtlq/KQuO8j0BBsUJYlRG8VBctKwYdwuBtajHgHTmSgUU3Oai+8oYN/rKCXwXzrUlYA+GiMgotAIbXY2BCGw==",
- "peer": true,
"dependencies": {
"@yr/monotone-cubic-spline": "^1.0.3",
"svg.draggable.js": "^2.2.2",
@@ -2347,7 +2347,8 @@
"node_modules/arg": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
- "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="
+ "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
+ "peer": true
},
"node_modules/argparse": {
"version": "2.0.1",
@@ -2550,6 +2551,7 @@
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "peer": true,
"engines": {
"node": ">=8"
}
@@ -2629,6 +2631,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
+ "peer": true,
"engines": {
"node": ">= 6"
}
@@ -2704,6 +2707,7 @@
"url": "https://paulmillr.com/funding/"
}
],
+ "peer": true,
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
@@ -2724,6 +2728,7 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "peer": true,
"dependencies": {
"is-glob": "^4.0.1"
},
@@ -2792,6 +2797,7 @@
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
+ "peer": true,
"engines": {
"node": ">= 6"
}
@@ -2863,6 +2869,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "peer": true,
"bin": {
"cssesc": "bin/cssesc"
},
@@ -2873,8 +2880,7 @@
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
- "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
- "peer": true
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
},
"node_modules/data-view-buffer": {
"version": "1.0.1",
@@ -3046,12 +3052,14 @@
"node_modules/didyoumean": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
- "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="
+ "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
+ "peer": true
},
"node_modules/dlv": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
- "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
+ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
+ "peer": true
},
"node_modules/doctrine": {
"version": "3.0.0",
@@ -3304,7 +3312,6 @@
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
"integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
"dev": true,
- "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
@@ -4261,6 +4268,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "peer": true,
"dependencies": {
"binary-extensions": "^2.0.0"
},
@@ -4655,6 +4663,7 @@
"version": "1.21.0",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz",
"integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==",
+ "peer": true,
"bin": {
"jiti": "bin/jiti.js"
}
@@ -4741,6 +4750,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
"integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
+ "peer": true,
"engines": {
"node": ">=10"
}
@@ -5701,6 +5711,7 @@
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+ "peer": true,
"dependencies": {
"any-promise": "^1.0.0",
"object-assign": "^4.0.1",
@@ -5744,6 +5755,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -5760,6 +5772,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
+ "peer": true,
"engines": {
"node": ">= 6"
}
@@ -6020,6 +6033,7 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+ "peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -6028,6 +6042,7 @@
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
"integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
+ "peer": true,
"engines": {
"node": ">= 6"
}
@@ -6060,7 +6075,6 @@
}
],
"license": "MIT",
- "peer": true,
"dependencies": {
"nanoid": "^3.3.8",
"picocolors": "^1.1.1",
@@ -6074,6 +6088,7 @@
"version": "15.1.0",
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
+ "peer": true,
"dependencies": {
"postcss-value-parser": "^4.0.0",
"read-cache": "^1.0.0",
@@ -6090,6 +6105,7 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
"integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
+ "peer": true,
"dependencies": {
"camelcase-css": "^2.0.1"
},
@@ -6118,6 +6134,7 @@
"url": "https://github.com/sponsors/ai"
}
],
+ "peer": true,
"dependencies": {
"lilconfig": "^3.0.0",
"yaml": "^2.3.4"
@@ -6142,6 +6159,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz",
"integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==",
+ "peer": true,
"engines": {
"node": ">=14"
}
@@ -6150,6 +6168,7 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz",
"integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==",
+ "peer": true,
"dependencies": {
"postcss-selector-parser": "^6.0.11"
},
@@ -6168,6 +6187,7 @@
"version": "6.0.13",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz",
"integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==",
+ "peer": true,
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
@@ -6179,7 +6199,8 @@
"node_modules/postcss-value-parser": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
- "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "peer": true
},
"node_modules/prelude-ls": {
"version": "1.2.1",
@@ -6195,7 +6216,6 @@
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz",
"integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==",
"dev": true,
- "peer": true,
"bin": {
"prettier": "bin-prettier.js"
},
@@ -6290,7 +6310,6 @@
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
- "peer": true,
"dependencies": {
"loose-envify": "^1.1.0"
},
@@ -6314,7 +6333,6 @@
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
- "peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
"scheduler": "^0.23.2"
@@ -6427,6 +6445,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
"integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
+ "peer": true,
"dependencies": {
"pify": "^2.3.0"
}
@@ -6435,6 +6454,7 @@
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "peer": true,
"dependencies": {
"picomatch": "^2.2.1"
},
@@ -6602,7 +6622,6 @@
"integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==",
"devOptional": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@types/estree": "1.0.8"
},
@@ -6955,6 +6974,7 @@
"version": "3.34.0",
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz",
"integrity": "sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==",
+ "peer": true,
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.2",
"commander": "^4.0.0",
@@ -6976,6 +6996,7 @@
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "peer": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -6996,6 +7017,7 @@
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
"license": "ISC",
+ "peer": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -7184,6 +7206,7 @@
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
+ "peer": true,
"dependencies": {
"any-promise": "^1.0.0"
}
@@ -7192,6 +7215,7 @@
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
"integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
+ "peer": true,
"dependencies": {
"thenify": ">= 3.1.0 < 4"
},
@@ -7243,7 +7267,6 @@
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=12"
},
@@ -7304,7 +7327,8 @@
"node_modules/ts-interface-checker": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
- "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
+ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
+ "peer": true
},
"node_modules/tslib": {
"version": "2.6.2",
@@ -7413,7 +7437,6 @@
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz",
"integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==",
"license": "Apache-2.0",
- "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -7538,7 +7561,8 @@
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "peer": true
},
"node_modules/vfile": {
"version": "6.0.3",
@@ -7677,7 +7701,6 @@
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=12"
},
@@ -7789,6 +7812,7 @@
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz",
"integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==",
"license": "ISC",
+ "peer": true,
"bin": {
"yaml": "bin.mjs"
},
diff --git a/test/factories/views.rb b/test/factories/views.rb
deleted file mode 100644
index 0dceb854..00000000
--- a/test/factories/views.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# frozen_string_literal: true
-
-FactoryBot.define do
- factory :view do
- account { nil }
- viewable { nil }
- end
-end
diff --git a/test/screenshots/dashboard-with-chart.png b/test/screenshots/dashboard-with-chart.png
index 6e195eed..e29a3def 100644
Binary files a/test/screenshots/dashboard-with-chart.png and b/test/screenshots/dashboard-with-chart.png differ
diff --git a/test/screenshots/metric-contributions.png b/test/screenshots/metric-contributions.png
index abb1fb2b..d0c07808 100644
Binary files a/test/screenshots/metric-contributions.png and b/test/screenshots/metric-contributions.png differ
diff --git a/test/support/screenshot_helpers.rb b/test/support/screenshot_helpers.rb
index e3560984..282c6427 100644
--- a/test/support/screenshot_helpers.rb
+++ b/test/support/screenshot_helpers.rb
@@ -11,13 +11,12 @@ def capture_screenshot(name)
return if ENV['CI']
disable_animations
- hide_toasts
target_path = SCREENSHOTS_DIR.join("#{name}.png")
FileUtils.mkdir_p(target_path.dirname)
Tempfile.create(['screenshot', '.png']) do |temp_file|
- page.save_screenshot(temp_file.path) # rubocop:disable Lint/Debugger
+ with_toasts_hidden { page.save_screenshot(temp_file.path) } # rubocop:disable Lint/Debugger
should_save = !File.exist?(target_path) || images_differ?(temp_file.path, target_path)
FileUtils.cp(temp_file.path, target_path) if should_save
@@ -39,10 +38,22 @@ def disable_animations
nil
end
- def hide_toasts
- page.execute_script("document.querySelector('[data-rht-toaster]')?.remove()")
- rescue StandardError # ignore if JS execution fails
- nil
+ # Hides react-hot-toast while yielding. Targets the Toaster container by
+ # its distinctive inline styles and toast items by their aria attributes,
+ # so no production-side hook is needed.
+ def with_toasts_hidden
+ page.execute_script(<<~JS)
+ const style = document.createElement('style');
+ style.id = 'hide-toasts';
+ style.textContent = [
+ 'div[style*="z-index: 9999"][style*="pointer-events: none"]',
+ '[role="status"][aria-live]'
+ ].join(',') + ' { display: none !important; }';
+ document.head.appendChild(style);
+ JS
+ yield
+ ensure
+ page.execute_script("document.getElementById('hide-toasts')?.remove()")
end
def images_differ?(new_image_path, existing_image_path)
diff --git a/test/system/metrics_test.rb b/test/system/metrics_test.rb
index a165f8f9..eb4048e1 100644
--- a/test/system/metrics_test.rb
+++ b/test/system/metrics_test.rb
@@ -142,14 +142,4 @@ class MetricsTest < ApplicationSystemTestCase
assert_text 'Metric deleted'
assert_equal 1, Metric.count
end
-
- it 'shows users who have seen a metric' do
- sign_in(user, to: user_projects_path)
- visit "/user/projects?project_id=#{project.id}&metric_id=#{eslint_metric.id}"
- assert_text 'Top Contributors'
- assert_no_text 'Seen by'
- visit "/user/projects?project_id=#{project.id}&metric_id=#{eslint_metric.id}"
- assert_text 'Top Contributors'
- assert_text 'Seen by'
- end
end