Files
akkounts/spec/features/services/rs_auths_spec.rb
T
raucao 17bad40bf4
CI / Test (pull_request) Successful in 48s
Release Drafter / Update release notes draft (pull_request) Successful in 3s
Show device timestamps in the reader's local time
The exact issuance and expiry times in the device rows' hover titles were
rendered in UTC, which reads as wrong to anyone not living there.

- Mark the dates with a <time> element carrying the absolute (UTC) timestamp,
  rather than a custom data attribute whose name claimed to hold local time
- Rewrite those titles in the browser's own zone, keeping the server's UTC
  rendering as the fallback for when JavaScript is unavailable
- Only convert values that carry an explicit offset, so a date-only <time> is
  not read as UTC midnight and shifted onto the previous day
- Give the revoke button a little more room from the device name and dates

Part of #254
2026-11-03 18:28:14 +04:00

207 lines
6.3 KiB
Ruby

require 'rails_helper'
RSpec.describe "Storage connected apps", type: :feature do
let(:user) { create :user }
let(:firefox_ua) do
"Mozilla/5.0 (X11; Linux x86_64; rv:120.0) Gecko/20100101 Firefox/120.0"
end
let(:chrome_ua) do
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 " \
"(KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"
end
before do
allow_any_instance_of(AppCatalog::WebApp).to receive(:update_metadata).and_return(true)
allow_any_instance_of(RemoteStorageAuthorization).to receive(:remove_token_expiry_job).and_return(nil)
allow_any_instance_of(User).to receive(:ldap_entry).and_return(
uid: user.cn, ou: user.ou, mail: user.email, admin: nil,
services_enabled: ["remotestorage"]
)
login_as user, scope: :user
end
def create_auth(client:, device:, permissions: %w(documents:rw))
user.remote_storage_authorizations.create!(
permissions: permissions,
client_id: client,
redirect_uri: "https://#{client}",
expire_at: 1.month.from_now,
user_agent: device
)
end
context "with a connected app" do
before do
AppCatalog::WebApp.create!(url: "https://notes.example.com", name: "Notes", metadata: {})
end
let!(:auth) do
create_auth(client: "notes.example.com", device: firefox_ua,
permissions: %w(documents:rw photos:r))
end
after { auth.destroy }
it "shows the app with device and category counts" do
visit apps_services_storage_path
within ".rs-auth-card" do
expect(page).to have_content("Notes")
expect(page).to have_content("notes.example.com")
expect(page).to have_content("1 device")
expect(page).to have_content("2 categories")
end
end
it "does not show the permission expiry on the card" do
visit apps_services_storage_path
within ".rs-auth-card" do
expect(page).not_to have_content("Expires")
end
end
it "renders access and per-token details in the modal" do
visit apps_services_storage_path
expect(page).to have_selector("p.scope", text: "documents", visible: false)
expect(page).to have_selector("p.scope", text: "photos", visible: false)
expect(page).to have_selector("h5", text: "Connected devices", visible: false)
expect(page).to have_selector("p", text: "Firefox on Linux", visible: false)
expect(page).to have_selector("p", text: /Expires/, visible: false)
expect(page).to have_button("Revoke access", visible: false)
expect(page).to have_link("Launch app", visible: false)
end
it "renders the ids that the revoke stream targets" do
visit apps_services_storage_path
expect(page).to have_selector(".rs-auth-card[id^='rs_app_card_']")
expect(page).to have_selector("div[id^='rs_app_access_']", visible: false)
expect(page).to have_selector(
"li##{ActionView::RecordIdentifier.dom_id(auth, :device)}", visible: false
)
end
it "marks the device timestamps with machine-readable UTC times" do
visit apps_services_storage_path
timestamps = page.all("time[datetime]", visible: false)
.map { |element| Time.iso8601(element["datetime"]) }
expect(timestamps).to contain_exactly(
be_within(1.second).of(auth.created_at),
be_within(1.second).of(auth.expire_at)
)
end
end
context "with multiple apps" do
let!(:notes) { create_auth(client: "notes.example.com", device: firefox_ua) }
let!(:photos) { create_auth(client: "photos.example.com", device: firefox_ua) }
after do
notes.destroy
photos.destroy
end
it "shows one card per app" do
visit apps_services_storage_path
expect(page).to have_selector(".rs-auth-card", count: 2)
end
end
context "with one app on multiple devices" do
let!(:laptop) { create_auth(client: "notes.example.com", device: firefox_ua) }
let!(:phone) { create_auth(client: "notes.example.com", device: chrome_ua) }
after do
laptop.destroy
phone.destroy
end
it "shows a single card with the device count" do
visit apps_services_storage_path
expect(page).to have_selector(".rs-auth-card", count: 1)
within ".rs-auth-card" do
expect(page).to have_content("2 devices")
end
end
end
context "with one app on multiple devices and differing scopes" do
let!(:laptop) do
create_auth(client: "notes.example.com", device: firefox_ua, permissions: %w(documents:rw))
end
let!(:phone) do
create_auth(client: "notes.example.com", device: chrome_ua,
permissions: %w(documents:rw photos:r))
end
after do
laptop.destroy
phone.destroy
end
it "summarises the union of the app's scopes in the card and the modal" do
visit apps_services_storage_path
within ".rs-auth-card" do
expect(page).to have_content("2 devices")
expect(page).to have_content("2 categories")
end
expect(page).to have_selector("p.scope", text: "documents", visible: false)
expect(page).to have_selector("p.scope", text: "photos", visible: false)
end
end
context "with an authorization from before user agents were recorded" do
let!(:legacy) { create_auth(client: "notes.example.com", device: nil) }
after { legacy.destroy }
it "only claims a lower-bound device count and labels the device as unknown" do
visit apps_services_storage_path
within ".rs-auth-card" do
expect(page).to have_content("1+ device")
end
expect(page).to have_selector("p", text: "Unknown device(s)", visible: false)
end
end
context "with an unrecorded authorization alongside a recorded device" do
let!(:legacy) { create_auth(client: "notes.example.com", device: nil) }
let!(:laptop) { create_auth(client: "notes.example.com", device: firefox_ua) }
after do
legacy.destroy
laptop.destroy
end
it "counts the unrecorded authorization as a possible extra device" do
visit apps_services_storage_path
within ".rs-auth-card" do
expect(page).to have_content("2+ devices")
end
end
end
context "without connected apps" do
it "shows the empty state" do
visit apps_services_storage_path
expect(page).to have_content("No apps connected")
end
end
end