Fix validation issues (#11050)

* Fix formatting

Signed-off-by: Artyom Savchenko <armisav@gmail.com>

* Fix svelte-check

Signed-off-by: Artyom Savchenko <armisav@gmail.com>

* Fix build

Signed-off-by: Artyom Savchenko <armisav@gmail.com>

* Fix formatting

Signed-off-by: Artyom Savchenko <armisav@gmail.com>

* Fix print resources

Signed-off-by: Artyom Savchenko <armisav@gmail.com>

---------

Signed-off-by: Artyom Savchenko <armisav@gmail.com>
This commit is contained in:
Artyom Savchenko
2026-09-30 05:12:59 +02:00
committed by GitHub
parent f030c2a337
commit 90d418c224
14 changed files with 230 additions and 92 deletions
@@ -16,10 +16,12 @@ describe('workspace favicon before application startup', () => {
expect(script).toBeDefined()
runInNewContext(script ?? '', {
location: { pathname },
localStorage: { getItem: (key: string) => {
if (denied) throw new Error('Storage denied')
return key === 'huly.workspace-favicon:/workbench/company-a' ? cached ?? null : null
} },
localStorage: {
getItem: (key: string) => {
if (denied) throw new Error('Storage denied')
return key === 'huly.workspace-favicon:/workbench/company-a' ? (cached ?? null) : null
}
},
document: {
getElementById: () => fallback,
createElement: () => ({}),
@@ -76,31 +78,43 @@ describe('workspace identification colour', () => {
describe('optional workspace favicon', () => {
const originalFetch = globalThis.fetch
afterEach(() => { globalThis.fetch = originalFetch })
afterEach(() => {
globalThis.fetch = originalFetch
})
it('does not load images when both options are disabled', async () => {
const fetchIcon = jest.fn()
globalThis.fetch = fetchIcon
await expect(renderWorkspaceIdentity('/workspace.png', null, undefined, {
syncLogo: false, showColor: false
})).resolves.toEqual({ color: '#64748b' })
await expect(
renderWorkspaceIdentity('/workspace.png', null, undefined, {
syncLogo: false,
showColor: false
})
).resolves.toEqual({ color: '#64748b' })
expect(fetchIcon).not.toHaveBeenCalled()
})
it('does not wait for a workspace logo when applying a manual badge to the site icon', async () => {
const fetchIcon = jest.fn().mockRejectedValue(new Error('Site icon unavailable'))
globalThis.fetch = fetchIcon
await expect(renderWorkspaceIdentity('/workspace.png', '#ff8800', undefined, {
syncLogo: false, showColor: true, defaultIconUrl: '/site.ico'
})).rejects.toThrow('Site icon unavailable')
await expect(
renderWorkspaceIdentity('/workspace.png', '#ff8800', undefined, {
syncLogo: false,
showColor: true,
defaultIconUrl: '/site.ico'
})
).rejects.toThrow('Site icon unavailable')
expect(fetchIcon).toHaveBeenCalledTimes(1)
expect(fetchIcon).toHaveBeenCalledWith('/site.ico', { signal: undefined })
})
it('falls back to the unchanged site favicon if the workspace logo is unavailable', async () => {
globalThis.fetch = jest.fn().mockRejectedValue(new Error('Workspace logo unavailable'))
await expect(renderWorkspaceIdentity('/workspace.png', null, undefined, {
syncLogo: true, showColor: false
})).resolves.toEqual({ color: '#64748b' })
await expect(
renderWorkspaceIdentity('/workspace.png', null, undefined, {
syncLogo: true,
showColor: false
})
).resolves.toEqual({ color: '#64748b' })
})
})
+1 -1
View File
@@ -22,7 +22,7 @@ async function resumeAudioContext (context: AudioContext): Promise<boolean> {
// Calling resume before the document has received user activation can leave
// its promise pending indefinitely because of the browser autoplay policy.
if (navigator.userActivation?.hasBeenActive === false) return false
if (!navigator.userActivation?.hasBeenActive) return false
let timeout: ReturnType<typeof setTimeout> | undefined
try {
+32 -17
View File
@@ -22,11 +22,18 @@ export function mixLogoColor (pixels: Uint8ClampedArray): string {
}
}
if (weight === 0) return defaultIdentityColor
return '#' + total.map((sum) => {
const linear = sum / weight
const srgb = linear <= 0.0031308 ? linear * 12.92 : 1.055 * linear ** (1 / 2.4) - 0.055
return Math.round(Math.max(0, Math.min(1, srgb)) * 255).toString(16).padStart(2, '0')
}).join('')
return (
'#' +
total
.map((sum) => {
const linear = sum / weight
const srgb = linear <= 0.0031308 ? linear * 12.92 : 1.055 * linear ** (1 / 2.4) - 0.055
return Math.round(Math.max(0, Math.min(1, srgb)) * 255)
.toString(16)
.padStart(2, '0')
})
.join('')
)
}
/** @public */
@@ -90,15 +97,16 @@ export function clearWorkspaceFaviconCache (ownerDocument: Document = document):
function getOriginalIcons (ownerDocument: Document): HTMLLinkElement[] {
let icons = originalIcons.get(ownerDocument)
if (icons === undefined) {
icons = Array.from(ownerDocument.head.querySelectorAll<HTMLLinkElement>('link[rel~="icon"]:not(#workspace-favicon)'))
.map((icon) => {
// The initial page keeps fallback icons inert while a cached workspace icon is visible.
if (icon.dataset.defaultHref === undefined) return icon
const original = icon.cloneNode(true) as HTMLLinkElement
original.href = new URL(icon.dataset.defaultHref, ownerDocument.baseURI).href
delete original.dataset.defaultHref
return original
})
icons = Array.from(
ownerDocument.head.querySelectorAll<HTMLLinkElement>('link[rel~="icon"]:not(#workspace-favicon)')
).map((icon) => {
// The initial page keeps fallback icons inert while a cached workspace icon is visible.
if (icon.dataset.defaultHref === undefined) return icon
const original = icon.cloneNode(true) as HTMLLinkElement
original.href = new URL(icon.dataset.defaultHref, ownerDocument.baseURI).href
delete original.dataset.defaultHref
return original
})
originalIcons.set(ownerDocument, icons)
}
return icons
@@ -192,7 +200,8 @@ export function createWorkspaceFavicon (ownerDocument: Document = document): {
const defaults = getOriginalIcons(ownerDocument)
const cacheKey = faviconCacheKey(ownerDocument)
const cacheRevision = cacheRevisions.get(ownerDocument) ?? 0
const link = ownerDocument.querySelector<HTMLLinkElement>('link#workspace-favicon') ?? ownerDocument.createElement('link')
const link =
ownerDocument.querySelector<HTMLLinkElement>('link#workspace-favicon') ?? ownerDocument.createElement('link')
link.rel = 'icon'
link.type = 'image/png'
link.sizes.value = '32x32'
@@ -225,8 +234,14 @@ export function createWorkspaceFavicon (ownerDocument: Document = document): {
request?.abort()
request = new AbortController()
try {
const result = await renderWorkspaceIdentity(logoUrl, color, request.signal,
options === undefined ? undefined : { ...options, defaultIconUrl: getDefaultWorkspaceFaviconUrl(ownerDocument) })
const result = await renderWorkspaceIdentity(
logoUrl,
color,
request.signal,
options === undefined
? undefined
: { ...options, defaultIconUrl: getDefaultWorkspaceFaviconUrl(ownerDocument) }
)
if (disposed || current !== revision || faviconCacheKey(ownerDocument) !== cacheKey) return
if (cacheRevision !== (cacheRevisions.get(ownerDocument) ?? 0)) return
if (result.favicon === undefined) {
@@ -28,7 +28,15 @@
* a prefix is only aliased (transformation 1), never wrapped (transformation
* 2). This matches user intent — they specified a field explicitly.
*/
import { escapeRegExp, fullTextSearchFields } from '@hcengineering/core'
import * as core from '@hcengineering/core'
// Keep standalone package checks independent of generated dependency declarations.
// Both exports are part of the current core source and runtime bundle, while an
// older ignored `core/types` directory may not describe them yet.
const { escapeRegExp, fullTextSearchFields } = core as typeof core & {
escapeRegExp: (value: string) => string
fullTextSearchFields: readonly string[]
}
export type SearchScope = 'title' | 'title-description' | 'all'
@@ -20,7 +20,7 @@ export const MILLISECONDS_IN_MINUTE = 60000
export const MILLISECONDS_IN_DAY = 86400000
export const MILLISECONDS_IN_WEEK = DAYS_IN_WEEK * MILLISECONDS_IN_DAY
export function firstDay(date: Date, firstDay: number = 1): Date {
export function firstDay (date: Date, firstDay: number = 1): Date {
const firstDayOfMonth = new Date(new Date(date).setHours(0, 0, 0, 0))
firstDayOfMonth.setDate(1) // First day of month
const result = new Date(firstDayOfMonth)
@@ -32,23 +32,23 @@ export function firstDay(date: Date, firstDay: number = 1): Date {
return result
}
export function getWeek(date: Date): number {
export function getWeek (date: Date): number {
const onejan = new Date(date.getFullYear(), 0, 1)
return Math.ceil(((date.getTime() - onejan.getTime()) / MILLISECONDS_IN_DAY + onejan.getDay() + 1) / DAYS_IN_WEEK)
}
export function daysInMonth(date: Date): number {
export function daysInMonth (date: Date): number {
return 33 - new Date(date.getFullYear(), date.getMonth(), 33).getDate()
}
export function getWeekDayName(weekDay: Date, weekFormat: 'narrow' | 'short' | 'long' | undefined = 'short'): string {
export function getWeekDayName (weekDay: Date, weekFormat: 'narrow' | 'short' | 'long' | undefined = 'short'): string {
const locale = new Intl.NumberFormat().resolvedOptions().locale
return new Intl.DateTimeFormat(locale, {
weekday: weekFormat
}).format(weekDay)
}
export function getWeekDayNames(): Map<number, string> {
export function getWeekDayNames (): Map<number, string> {
const today: Date = new Date()
const offset: number = 0 - today.getDay()
const startDate: number = today.setTime(today.getTime() + MILLISECONDS_IN_DAY * offset)
@@ -59,15 +59,15 @@ export function getWeekDayNames(): Map<number, string> {
return result
}
export function day(firstDay: Date, offset: number): Date {
export function day (firstDay: Date, offset: number): Date {
return new Date(new Date(firstDay).setDate(firstDay.getDate() + offset))
}
export function weekday(firstDay: Date, w: number, d: number): Date {
export function weekday (firstDay: Date, w: number, d: number): Date {
return day(firstDay, w * DAYS_IN_WEEK + d)
}
export function areDatesEqual(firstDate: Date | undefined, secondDate: Date | undefined): boolean {
export function areDatesEqual (firstDate: Date | undefined, secondDate: Date | undefined): boolean {
if (firstDate === undefined || secondDate === undefined) {
return false
}
@@ -78,19 +78,19 @@ export function areDatesEqual(firstDate: Date | undefined, secondDate: Date | un
)
}
export function fromCurrentToTz(date: Date | number, tz: string): Date {
export function fromCurrentToTz (date: Date | number, tz: string): Date {
return utcToZonedTime(zonedTimeToUtc(date, getUserTimezone()), tz)
}
export function fromTzToCurrent(date: Date | number, tz: string): Date {
export function fromTzToCurrent (date: Date | number, tz: string): Date {
return utcToZonedTime(zonedTimeToUtc(date, tz), getUserTimezone())
}
export function isWeekend(date: Date): boolean {
export function isWeekend (date: Date): boolean {
return date.getDay() === 0 || date.getDay() === 6
}
export function getMonthName(date: Date, option: 'narrow' | 'short' | 'long' | 'numeric' | '2-digit' = 'long'): string {
export function getMonthName (date: Date, option: 'narrow' | 'short' | 'long' | 'numeric' | '2-digit' = 'long'): string {
try {
const locale = new Intl.NumberFormat().resolvedOptions().locale
return new Intl.DateTimeFormat(locale, { month: option }).format(date)
@@ -106,24 +106,24 @@ export interface ICell {
classes: TCellStyle
}
export function getLocalWeekStart(): number {
export function getLocalWeekStart (): number {
const locale = new Intl.Locale(navigator.language)
return typeof (locale as any)?.getWeekInfo === 'function'
? (locale as any)?.getWeekInfo()?.firstDay
: ((locale as any).weekInfo?.firstDay ?? 1)
}
export function hasLocalWeekStart(): boolean {
export function hasLocalWeekStart (): boolean {
const locale = new Intl.Locale(navigator.language)
return typeof (locale as any)?.getWeekInfo === 'function' || (locale as any).weekInfo?.firstDay !== undefined
}
export function getWeekStart(date: Date = new Date(), firstDay: number = 1): Date {
export function getWeekStart (date: Date = new Date(), firstDay: number = 1): Date {
date = new Date(new Date(date).setHours(0, 0, 0, 0))
return new Date(date.setDate(date.getDate() - ((date.getDay() - firstDay + 7) % 7)))
}
export function addZero(value: number): string {
export function addZero (value: number): string {
if (value < 10) {
return `0${value}`
}
@@ -155,11 +155,11 @@ export const getDueDateIconModifier = (
return 'normal'
}
export function getFormattedDate(value: number | null, options?: Intl.DateTimeFormatOptions): string {
export function getFormattedDate (value: number | null, options?: Intl.DateTimeFormatOptions): string {
return value === null ? '' : new Date(value).toLocaleString('default', options ?? { month: 'short', day: 'numeric' })
}
export function formatDate(date: Date, showYear: boolean): string {
export function formatDate (date: Date, showYear: boolean): string {
const locale = new Intl.NumberFormat().resolvedOptions().locale
const options: Intl.DateTimeFormatOptions = showYear
? { year: 'numeric', month: 'short', day: 'numeric' }
@@ -185,7 +185,7 @@ export const convertTimeZone = (tz: string): TimeZone => {
}
}
export function getUserTimezone(): string {
export function getUserTimezone (): string {
if (window.Intl !== undefined) {
return Intl.DateTimeFormat().resolvedOptions().timeZone
} else {
@@ -43,7 +43,7 @@
} catch (err) {
if (!controller.signal.aborted && file === value) {
failed = true
Analytics.handleError(err)
Analytics.handleError(err instanceof Error ? err : new Error(String(err)))
}
} finally {
if (!controller.signal.aborted && file === value) {
@@ -110,9 +110,13 @@
const client = getClient()
const workspaceSettingsQuery = createQuery()
workspaceSettingsQuery.query(settingsRes.class.WorkspaceSetting, { _id: settingsRes.ids.WorkspaceSetting }, (result) => {
workspaceSettings = result[0]
})
workspaceSettingsQuery.query(
settingsRes.class.WorkspaceSetting,
{ _id: settingsRes.ids.WorkspaceSetting },
(result) => {
workspaceSettings = result[0]
}
)
async function handleAvatarDone (): Promise<void> {
const existing = await client.findOne(settingsRes.class.WorkspaceSetting, { _id: settingsRes.ids.WorkspaceSetting })
@@ -2,8 +2,13 @@
<script lang="ts">
import core, { AccountRole, getCurrentAccount } from '@hcengineering/core'
import {
defaultIdentityColor, getClient, getFileUrl, getDefaultWorkspaceFaviconUrl, normalizeIdentityColor,
renderWorkspaceIdentity, type WorkspaceIdentityImage
defaultIdentityColor,
getClient,
getFileUrl,
getDefaultWorkspaceFaviconUrl,
normalizeIdentityColor,
renderWorkspaceIdentity,
type WorkspaceIdentityImage
} from '@hcengineering/presentation'
import setting, { type WorkspaceSetting } from '@hcengineering/setting'
import { Button, Label, Toggle } from '@hcengineering/ui'
@@ -27,7 +32,12 @@
$: logoUrl = workspaceSetting?.icon != null ? getFileUrl(workspaceSetting.icon) : undefined
$: void updatePreview(logoUrl, manualColor, syncLogo, showColor)
async function updatePreview (url: string | undefined, color: string | undefined, syncLogo: boolean, showColor: boolean): Promise<void> {
async function updatePreview (
url: string | undefined,
color: string | undefined,
syncLogo: boolean,
showColor: boolean
): Promise<void> {
const current = ++revision
request?.abort()
request = new AbortController()
@@ -48,7 +58,9 @@
if (color !== undefined) await save({ identificationColor: color })
}
async function save (patch: Partial<Pick<WorkspaceSetting, 'identificationColor' | 'syncWorkspaceLogo' | 'identificationColorEnabled'>>): Promise<void> {
async function save (
patch: Partial<Pick<WorkspaceSetting, 'identificationColor' | 'syncWorkspaceLogo' | 'identificationColorEnabled'>>
): Promise<void> {
if (!canEdit || busy) return
const focused = document.activeElement as HTMLElement | null
const focusLabel = focused?.closest('label')?.getAttribute('aria-labelledby')
@@ -57,8 +69,12 @@
try {
const existing = await client.findOne(setting.class.WorkspaceSetting, { _id: setting.ids.WorkspaceSetting })
if (existing === undefined) {
await client.createDoc(setting.class.WorkspaceSetting, core.space.Workspace,
patch, setting.ids.WorkspaceSetting)
await client.createDoc(
setting.class.WorkspaceSetting,
core.space.Workspace,
patch,
setting.ids.WorkspaceSetting
)
} else {
await client.diffUpdate(existing, patch)
}
@@ -90,8 +106,12 @@
<span id="workspace-sync-logo-label"><Label label={setting.string.SyncWorkspaceLogo} /></span>
<div class="controls">
{#key saveRevision}
<Toggle on={syncLogo} disabled={!canEdit || busy} aria-labelledby="workspace-sync-logo-label"
on:change={(event) => save({ syncWorkspaceLogo: event.detail })} />
<Toggle
on={syncLogo}
disabled={!canEdit || busy}
aria-labelledby="workspace-sync-logo-label"
on:change={(event) => save({ syncWorkspaceLogo: event.detail })}
/>
{/key}
</div>
</div>
@@ -99,8 +119,12 @@
<span id="workspace-color-label"><Label label={setting.string.IdentificationColor} /></span>
<div class="controls">
{#key saveRevision}
<Toggle on={showColor} disabled={!canEdit || busy} aria-labelledby="workspace-color-label"
on:change={(event) => save({ identificationColorEnabled: event.detail })} />
<Toggle
on={showColor}
disabled={!canEdit || busy}
aria-labelledby="workspace-color-label"
on:change={(event) => save({ identificationColorEnabled: event.detail })}
/>
{/key}
{#if showColor}
<div class="color-control" role="group" aria-labelledby="workspace-color-label">
@@ -108,12 +132,23 @@
<span class="color-swatch" style:background={preview.color} />
<code>{preview.color.toUpperCase()}</code>
<span class="sr-only"><Label label={setting.string.IdentificationColor} /></span>
<input bind:this={colorInput} type="color" value={preview.color} disabled={!canEdit || busy}
on:change={(event) => saveColor(event.currentTarget.value)} />
<input
bind:this={colorInput}
type="color"
value={preview.color}
disabled={!canEdit || busy}
on:change={(event) => saveColor(event.currentTarget.value)}
/>
</label>
<span class="color-reset">
<Button kind="ghost" size="small" label={setting.string.ColorDefault} padding="0 .5rem"
disabled={!canEdit || busy || manualColor === undefined} on:click={() => saveColor(null)} />
<Button
kind="ghost"
size="small"
label={setting.string.ColorDefault}
padding="0 .5rem"
disabled={!canEdit || busy || manualColor === undefined}
on:click={() => saveColor(null)}
/>
</span>
</div>
{/if}
@@ -124,10 +159,30 @@
</div>
<style>
.identity { display: flex; flex-direction: column; gap: 1rem; }
.setting-row { display: grid; grid-template-columns: min(11rem, 45%) minmax(0, 1fr); align-items: center; gap: 0.75rem; min-height: 2rem; }
.controls { display: flex; align-items: center; flex-wrap: wrap; gap: 0.75rem; min-width: 0; }
.setting-row :global(.toggle:focus-within) { outline: 2px solid var(--primary-button-outline); outline-offset: 3px; border-radius: 1rem; }
.identity {
display: flex;
flex-direction: column;
gap: 1rem;
}
.setting-row {
display: grid;
grid-template-columns: min(11rem, 45%) minmax(0, 1fr);
align-items: center;
gap: 0.75rem;
min-height: 2rem;
}
.controls {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 0.75rem;
min-width: 0;
}
.setting-row :global(.toggle:focus-within) {
outline: 2px solid var(--primary-button-outline);
outline-offset: 3px;
border-radius: 1rem;
}
.color-control {
display: inline-flex;
align-items: center;
@@ -145,7 +200,10 @@
border-radius: 0.25rem;
min-width: 0;
}
.color-value:focus-within { outline: 2px solid var(--theme-content-color); outline-offset: 2px; }
.color-value:focus-within {
outline: 2px solid var(--theme-content-color);
outline-offset: 2px;
}
.color-swatch {
flex-shrink: 0;
width: 1.25rem;
@@ -153,9 +211,31 @@
border-radius: 0.25rem;
box-shadow: inset 0 0 0 1px var(--theme-divider-color);
}
.color-reset { border-left: 1px solid var(--theme-divider-color); padding-left: 0.125rem; }
input[type='color'] { position: absolute; inset: 0; opacity: 0; width: 100%; height: 100%; cursor: pointer; }
code { font-size: 0.8rem; }
.error { color: var(--theme-error-color, #e05252); font-size: 0.8125rem; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.color-reset {
border-left: 1px solid var(--theme-divider-color);
padding-left: 0.125rem;
}
input[type='color'] {
position: absolute;
inset: 0;
opacity: 0;
width: 100%;
height: 100%;
cursor: pointer;
}
code {
font-size: 0.8rem;
}
.error {
color: var(--theme-error-color, #e05252);
font-size: 0.8125rem;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
}
</style>
@@ -190,7 +190,7 @@ describe('buildRecipientBundles — end-to-end', () => {
expect(bundles.size).toBe(1)
const bundle = bundles.get(userB) ?? []
expect(bundle.length).toBe(2)
expect(bundle.map((b) => b.identifier).sort()).toEqual(['PROJ-2', 'PROJ-3'])
expect(bundle.map((b) => b.identifier).sort((a, b) => a.localeCompare(b))).toEqual(['PROJ-2', 'PROJ-3'])
})
it('produces zero bundles when only the trigger user has stake in any shift', () => {
@@ -83,6 +83,8 @@
dispatch('update', { key: model.key, value })
}
type DependentViewOption = ViewOptionModel & { dependsOn?: string }
// checking if selector provides multiple choice options
const hasMultipleSelections = (varTocheck: any) => {
if (!varTocheck) return false
@@ -95,8 +97,9 @@
$: visibleOthers = config.other.filter((p) => {
if (p.hidden?.(viewOptions) === true) return false
if (hideKeys.includes(p.key)) return false
if (p.dependsOn != null) {
const parentValue = (viewOptions as Record<string, unknown>)?.[p.dependsOn]
const { dependsOn } = p as DependentViewOption
if (dependsOn != null) {
const parentValue = (viewOptions as Record<string, unknown>)?.[dependsOn]
if (parentValue !== true) return false
}
return true
+8 -1
View File
@@ -19,7 +19,14 @@ import { type PresentationMiddlewareCreator } from '@hcengineering/presentation/
import { type AnyComponent } from '@hcengineering/ui/src/types'
import view, { viewId } from '@hcengineering/view'
export default mergeIds(viewId, view, {
// Keep standalone package checks independent of generated dependency declarations.
// ConfigureColumns is already registered by the current @hcengineering/view
// runtime; an older ignored `view/types` directory may not describe it yet.
const typedView = view as typeof view & {
string: typeof view.string & { ConfigureColumns: IntlString }
}
export default mergeIds(viewId, typedView, {
component: {
ObjectFilter: '' as AnyComponent,
DateFilter: '' as AnyComponent,
@@ -13,10 +13,12 @@
settingsLoaded = true
})
$: logoUrl = workspaceSetting?.icon != null ? getFileUrl(workspaceSetting.icon) : undefined
$: if (settingsLoaded) void favicon?.update(logoUrl, workspaceSetting?.identificationColor, {
syncLogo: workspaceSetting?.syncWorkspaceLogo === true,
showColor: workspaceSetting?.identificationColorEnabled === true
})
$: if (settingsLoaded) {
void favicon?.update(logoUrl, workspaceSetting?.identificationColor, {
syncLogo: workspaceSetting?.syncWorkspaceLogo === true,
showColor: workspaceSetting?.identificationColorEnabled === true
})
}
onMount(() => {
favicon = createWorkspaceFavicon()
+6 -1
View File
@@ -29,7 +29,12 @@ import type {
import core, { hasAccountRole } from '@hcengineering/core'
import login from '@hcengineering/login'
import { getMetadata, getResource, setMetadata } from '@hcengineering/platform'
import presentation, { clearWorkspaceFaviconCache, closeClient, getClient, setPresentationCookie } from '@hcengineering/presentation'
import presentation, {
clearWorkspaceFaviconCache,
closeClient,
getClient,
setPresentationCookie
} from '@hcengineering/presentation'
import {
closePanel,
getCurrentLocation,
@@ -88,7 +88,7 @@ describe('rest-api-server', () => {
const person = await accountClient.getPerson()
const socialIds: SocialId[] = await accountClient.getSocialIds(true)
// Ensure employee is created
// Employee mixin creation is privileged; use the system token only for test setup.
await ensureEmployee(
testCtx,
@@ -99,7 +99,7 @@ describe('rest-api-server', () => {
socialIds: socialIds.map((si) => si._id),
fullSocialIds: socialIds
},
connect(),
connect(apiWorkspace1, true),
socialIds,
async () => person
)
@@ -113,7 +113,7 @@ describe('rest-api-server', () => {
socialIds: socialIds.map((si) => si._id),
fullSocialIds: socialIds
},
connect(apiWorkspace2),
connect(apiWorkspace2, true),
socialIds,
async () => person
)