mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 14:30:41 +00:00
test: add ShareWorkflow dialog E2E tests (DLG-05) (#10588)
## Summary Adds Playwright E2E tests for the ShareWorkflow dialog component and its various states. ## Tests added - Dialog opens and shows unsaved state for new workflows (save prompt) - Ready state shows create link button for saved but unpublished workflows - Shared state shows copy URL field with share link after publishing - Stale state shows update link button when workflow modified after publishing - Close button dismisses dialog - Create link transitions dialog from ready to shared state - Tab switching between share link and publish to hub (when comfyHubUploadEnabled) - Tab aria-selected states update correctly on switch ## Approach - Share dialog is gated behind `isCloud` (compile-time constant), so tests invoke it directly via `page.evaluate()` importing `useShareDialog` - Share service API calls (`/api/userdata/*/publish`, `/api/assets/from-workflow`) mocked via `page.route()` for deterministic state testing - Dialog state (loading → unsaved → ready → shared → stale) controlled by mock responses - Feature flags set via `serverFeatureFlags.value` for tab visibility testing ## Notes - All pre-existing TS2307 errors are `.vue` module resolution — no new type errors - Tests cover the 5 dialog states: loading, unsaved, ready, shared, stale ## Task Part of Test Coverage Q2 Overhaul (DLG-05). ## Conventions - Uses Vue nodes with new menu enabled - Tests read as user stories - No full-page screenshots - Proper waits, no sleeps - All API calls mocked ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-10588-test-add-ShareWorkflow-dialog-E2E-tests-DLG-05-3306d73d365081a0ab15f333707e493b) by [Unito](https://www.unito.io) --------- Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
@@ -34,6 +34,7 @@ import { createAssetHelper } from '@e2e/fixtures/helpers/AssetHelper'
|
||||
import { AssetsHelper } from '@e2e/fixtures/helpers/AssetsHelper'
|
||||
import { CanvasHelper } from '@e2e/fixtures/helpers/CanvasHelper'
|
||||
import { ClipboardHelper } from '@e2e/fixtures/helpers/ClipboardHelper'
|
||||
import { CloudAuthHelper } from '@e2e/fixtures/helpers/CloudAuthHelper'
|
||||
import { CommandHelper } from '@e2e/fixtures/helpers/CommandHelper'
|
||||
import { DragDropHelper } from '@e2e/fixtures/helpers/DragDropHelper'
|
||||
import { FeatureFlagHelper } from '@e2e/fixtures/helpers/FeatureFlagHelper'
|
||||
@@ -181,6 +182,7 @@ export class ComfyPage {
|
||||
public readonly assets: AssetsHelper
|
||||
public readonly assetApi: AssetHelper
|
||||
public readonly modelLibrary: ModelLibraryHelper
|
||||
public readonly cloudAuth: CloudAuthHelper
|
||||
|
||||
/** Worker index to test user ID */
|
||||
public readonly userIds: string[] = []
|
||||
@@ -232,6 +234,7 @@ export class ComfyPage {
|
||||
this.assets = new AssetsHelper(page)
|
||||
this.assetApi = createAssetHelper(page)
|
||||
this.modelLibrary = new ModelLibraryHelper(page)
|
||||
this.cloudAuth = new CloudAuthHelper(page)
|
||||
}
|
||||
|
||||
get visibleToasts() {
|
||||
@@ -444,6 +447,10 @@ export const comfyPageFixture = base.extend<{
|
||||
console.error(e)
|
||||
}
|
||||
|
||||
if (testInfo.tags.includes('@cloud')) {
|
||||
await comfyPage.cloudAuth.mockAuth()
|
||||
}
|
||||
|
||||
await comfyPage.setup()
|
||||
|
||||
const needsPerf =
|
||||
|
||||
171
browser_tests/fixtures/helpers/CloudAuthHelper.ts
Normal file
171
browser_tests/fixtures/helpers/CloudAuthHelper.ts
Normal file
@@ -0,0 +1,171 @@
|
||||
import type { Page } from '@playwright/test'
|
||||
|
||||
/**
|
||||
* Mocks Firebase authentication for cloud E2E tests.
|
||||
*
|
||||
* The cloud build's router guard waits for Firebase `onAuthStateChanged`
|
||||
* to fire, then checks `getAuthHeader()`. In CI no Firebase project is
|
||||
* configured, so the user is never authenticated and the app redirects
|
||||
* to `/cloud/login`.
|
||||
*
|
||||
* This helper seeds Firebase's IndexedDB persistence layer with a mock
|
||||
* user and intercepts the Firebase REST APIs (securetoken, identitytoolkit)
|
||||
* so the SDK believes a user is signed in. Must be called before navigation.
|
||||
*/
|
||||
export class CloudAuthHelper {
|
||||
constructor(private readonly page: Page) {}
|
||||
|
||||
/**
|
||||
* Set up all auth mocks. Must be called before `comfyPage.setup()`.
|
||||
*/
|
||||
async mockAuth(): Promise<void> {
|
||||
await this.seedFirebaseIndexedDB()
|
||||
await this.mockFirebaseEndpoints()
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to a lightweight same-origin page to seed Firebase's
|
||||
* IndexedDB persistence with a mock user. This ensures the data
|
||||
* is written before the app loads and Firebase reads it.
|
||||
*
|
||||
* Firebase auth uses `browserLocalPersistence` which stores data in
|
||||
* IndexedDB database `firebaseLocalStorageDb`, object store
|
||||
* `firebaseLocalStorage`, keyed by `firebase:authUser:<apiKey>:<appName>`.
|
||||
*/
|
||||
private async seedFirebaseIndexedDB(): Promise<void> {
|
||||
// Navigate to a lightweight endpoint to get a same-origin context
|
||||
await this.page.goto('http://localhost:8188/api/users')
|
||||
|
||||
await this.page.evaluate(() => {
|
||||
const MOCK_USER_DATA = {
|
||||
uid: 'test-user-e2e',
|
||||
email: 'e2e@test.comfy.org',
|
||||
displayName: 'E2E Test User',
|
||||
emailVerified: true,
|
||||
isAnonymous: false,
|
||||
providerData: [
|
||||
{
|
||||
providerId: 'google.com',
|
||||
uid: 'test-user-e2e',
|
||||
displayName: 'E2E Test User',
|
||||
email: 'e2e@test.comfy.org',
|
||||
phoneNumber: null,
|
||||
photoURL: null
|
||||
}
|
||||
],
|
||||
stsTokenManager: {
|
||||
refreshToken: 'mock-refresh-token',
|
||||
accessToken: 'mock-firebase-id-token',
|
||||
expirationTime: Date.now() + 60 * 60 * 1000
|
||||
},
|
||||
apiKey: 'AIzaSyDa_YMeyzV0SkVe92vBZ1tVikWBmOU5KVE',
|
||||
appName: '[DEFAULT]'
|
||||
}
|
||||
|
||||
const DB_NAME = 'firebaseLocalStorageDb'
|
||||
const STORE_NAME = 'firebaseLocalStorage'
|
||||
const KEY = `firebase:authUser:${MOCK_USER_DATA.apiKey}:${MOCK_USER_DATA.appName}`
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const request = indexedDB.open(DB_NAME)
|
||||
request.onerror = () => reject(request.error)
|
||||
request.onupgradeneeded = () => {
|
||||
const db = request.result
|
||||
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
||||
db.createObjectStore(STORE_NAME)
|
||||
}
|
||||
}
|
||||
request.onsuccess = () => {
|
||||
const db = request.result
|
||||
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
||||
db.close()
|
||||
const upgradeReq = indexedDB.open(DB_NAME, db.version + 1)
|
||||
upgradeReq.onerror = () => reject(upgradeReq.error)
|
||||
upgradeReq.onupgradeneeded = () => {
|
||||
const upgradedDb = upgradeReq.result
|
||||
if (!upgradedDb.objectStoreNames.contains(STORE_NAME)) {
|
||||
upgradedDb.createObjectStore(STORE_NAME)
|
||||
}
|
||||
}
|
||||
upgradeReq.onsuccess = () => {
|
||||
const upgradedDb = upgradeReq.result
|
||||
const tx = upgradedDb.transaction(STORE_NAME, 'readwrite')
|
||||
tx.objectStore(STORE_NAME).put(
|
||||
{ fpiVersion: '1', value: MOCK_USER_DATA },
|
||||
KEY
|
||||
)
|
||||
tx.oncomplete = () => {
|
||||
upgradedDb.close()
|
||||
resolve()
|
||||
}
|
||||
tx.onerror = () => reject(tx.error)
|
||||
}
|
||||
return
|
||||
}
|
||||
const tx = db.transaction(STORE_NAME, 'readwrite')
|
||||
tx.objectStore(STORE_NAME).put(
|
||||
{ fpiVersion: '1', value: MOCK_USER_DATA },
|
||||
KEY
|
||||
)
|
||||
tx.oncomplete = () => {
|
||||
db.close()
|
||||
resolve()
|
||||
}
|
||||
tx.onerror = () => reject(tx.error)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercept Firebase Auth REST API endpoints so the SDK can
|
||||
* "refresh" the mock user's token without real credentials.
|
||||
*/
|
||||
private async mockFirebaseEndpoints(): Promise<void> {
|
||||
await this.page.route('**/securetoken.googleapis.com/**', (route) =>
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({
|
||||
access_token: 'mock-access-token',
|
||||
expires_in: '3600',
|
||||
token_type: 'Bearer',
|
||||
refresh_token: 'mock-refresh-token',
|
||||
id_token: 'mock-firebase-id-token',
|
||||
user_id: 'test-user-e2e',
|
||||
project_id: 'dreamboothy-dev'
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
await this.page.route('**/identitytoolkit.googleapis.com/**', (route) =>
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({
|
||||
kind: 'identitytoolkit#GetAccountInfoResponse',
|
||||
users: [
|
||||
{
|
||||
localId: 'test-user-e2e',
|
||||
email: 'e2e@test.comfy.org',
|
||||
displayName: 'E2E Test User',
|
||||
emailVerified: true,
|
||||
validSince: '0',
|
||||
lastLoginAt: String(Date.now()),
|
||||
createdAt: String(Date.now()),
|
||||
lastRefreshAt: new Date().toISOString()
|
||||
}
|
||||
]
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
await this.page.route('**/__/auth/**', (route) =>
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'text/html',
|
||||
body: '<html><body></body></html>'
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,8 @@ export const TestIds = {
|
||||
missingMediaStatusCard: 'missing-media-status-card',
|
||||
missingMediaConfirmButton: 'missing-media-confirm-button',
|
||||
missingMediaCancelButton: 'missing-media-cancel-button',
|
||||
missingMediaLocateButton: 'missing-media-locate-button'
|
||||
missingMediaLocateButton: 'missing-media-locate-button',
|
||||
publishTabPanel: 'publish-tab-panel'
|
||||
},
|
||||
keybindings: {
|
||||
presetMenu: 'keybinding-preset-menu'
|
||||
|
||||
352
browser_tests/tests/dialogs/shareWorkflowDialog.spec.ts
Normal file
352
browser_tests/tests/dialogs/shareWorkflowDialog.spec.ts
Normal file
@@ -0,0 +1,352 @@
|
||||
import type { Page } from '@playwright/test'
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
import type { AssetInfo } from '../../../src/schemas/apiSchema'
|
||||
import { comfyPageFixture } from '../../fixtures/ComfyPage'
|
||||
import { TestIds } from '../../fixtures/selectors'
|
||||
|
||||
interface PublishRecord {
|
||||
workflow_id: string
|
||||
share_id: string | null
|
||||
listed: boolean
|
||||
publish_time: string | null
|
||||
}
|
||||
|
||||
const PUBLISHED_RECORD: PublishRecord = {
|
||||
workflow_id: 'wf-1',
|
||||
share_id: 'share-abc',
|
||||
listed: false,
|
||||
publish_time: new Date(Date.now() + 60_000).toISOString()
|
||||
}
|
||||
|
||||
const PRIVATE_ASSET: AssetInfo = {
|
||||
id: 'asset-1',
|
||||
name: 'photo.png',
|
||||
preview_url: '',
|
||||
storage_url: '',
|
||||
model: false,
|
||||
public: false,
|
||||
in_library: false
|
||||
}
|
||||
|
||||
const test = comfyPageFixture
|
||||
|
||||
/**
|
||||
* Enable the workflow_sharing_enabled server feature flag at runtime.
|
||||
* FeatureFlagHelper.mockServerFeatures() intercepts `/api/features` but the
|
||||
* flags are already loaded by the time tests run, so direct mutation of the
|
||||
* reactive ref is the only reliable approach for server-side flags.
|
||||
*/
|
||||
async function enableWorkflowSharing(page: Page): Promise<void> {
|
||||
await page.evaluate(() => {
|
||||
const api = window.app!.api
|
||||
api.serverFeatureFlags.value = {
|
||||
...api.serverFeatureFlags.value,
|
||||
workflow_sharing_enabled: true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function mockPublishStatus(
|
||||
page: Page,
|
||||
record: PublishRecord | null
|
||||
): Promise<void> {
|
||||
await page.route('**/api/userdata/*/publish', async (route) => {
|
||||
if (route.request().method() === 'GET') {
|
||||
if (!record || !record.share_id) {
|
||||
await route.fulfill({ status: 404, body: 'Not found' })
|
||||
} else {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify(record)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
await route.fallback()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function mockPublishWorkflow(
|
||||
page: Page,
|
||||
result: PublishRecord
|
||||
): Promise<void> {
|
||||
await page.route('**/api/userdata/*/publish', async (route) => {
|
||||
if (route.request().method() === 'POST') {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify(result)
|
||||
})
|
||||
} else {
|
||||
await route.fallback()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function mockShareableAssets(
|
||||
page: Page,
|
||||
assets: AssetInfo[] = []
|
||||
): Promise<void> {
|
||||
await page.route('**/api/assets/from-workflow', async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({ assets })
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismiss stale PrimeVue dialog masks left by cloud-mode's onboarding flow
|
||||
* or auth-triggered modals by pressing Escape until they clear.
|
||||
*/
|
||||
async function dismissOverlays(page: Page): Promise<void> {
|
||||
const mask = page.locator('.p-dialog-mask')
|
||||
for (let attempt = 0; attempt < 3; attempt++) {
|
||||
if ((await mask.count()) === 0) break
|
||||
await page.keyboard.press('Escape')
|
||||
await mask
|
||||
.first()
|
||||
.waitFor({ state: 'hidden', timeout: 2000 })
|
||||
.catch(() => {})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the active workflow via the topbar Save menu action.
|
||||
* Mocks the userdata POST endpoint to avoid real server calls in tests.
|
||||
*/
|
||||
async function saveAndWait(
|
||||
comfyPage: {
|
||||
page: Page
|
||||
menu: { topbar: { saveWorkflow: (name: string) => Promise<void> } }
|
||||
},
|
||||
workflowName: string
|
||||
): Promise<void> {
|
||||
const filename =
|
||||
workflowName + (workflowName.endsWith('.json') ? '' : '.json')
|
||||
await comfyPage.page.route(
|
||||
/\/api\/userdata\/workflows(%2F|\/).*$/,
|
||||
async (route) => {
|
||||
if (route.request().method() === 'POST') {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({
|
||||
path: `workflows/${filename}`,
|
||||
size: 1024,
|
||||
modified: Date.now()
|
||||
})
|
||||
})
|
||||
} else {
|
||||
await route.fallback()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
await comfyPage.menu.topbar.saveWorkflow(workflowName)
|
||||
}
|
||||
|
||||
async function openShareDialog(page: Page): Promise<void> {
|
||||
await enableWorkflowSharing(page)
|
||||
await dismissOverlays(page)
|
||||
const shareButton = page.getByRole('button', { name: 'Share workflow' })
|
||||
await shareButton.click()
|
||||
}
|
||||
|
||||
function getShareDialog(page: Page) {
|
||||
return page.getByRole('dialog')
|
||||
}
|
||||
|
||||
test.describe('Share Workflow Dialog', { tag: '@cloud' }, () => {
|
||||
test('should show unsaved state for a new workflow', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const { page } = comfyPage
|
||||
|
||||
await mockPublishStatus(page, null)
|
||||
await mockShareableAssets(page)
|
||||
await openShareDialog(page)
|
||||
|
||||
const dialog = getShareDialog(page)
|
||||
await expect(dialog).toBeVisible()
|
||||
await expect(
|
||||
dialog.getByRole('button', { name: /save workflow/i })
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('should show ready state with create link button', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const { page } = comfyPage
|
||||
const workflowName = 'share-test-ready'
|
||||
|
||||
await saveAndWait(comfyPage, workflowName)
|
||||
|
||||
await mockPublishStatus(page, null)
|
||||
await mockShareableAssets(page)
|
||||
await openShareDialog(page)
|
||||
|
||||
const dialog = getShareDialog(page)
|
||||
await expect(dialog).toBeVisible()
|
||||
await expect(
|
||||
dialog.getByRole('button', { name: /create a link/i })
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('should show shared state with copy URL after publishing', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const { page } = comfyPage
|
||||
const workflowName = 'share-test-shared'
|
||||
|
||||
await saveAndWait(comfyPage, workflowName)
|
||||
|
||||
await mockPublishStatus(page, PUBLISHED_RECORD)
|
||||
await mockShareableAssets(page)
|
||||
await openShareDialog(page)
|
||||
|
||||
const dialog = getShareDialog(page)
|
||||
await expect(dialog).toBeVisible()
|
||||
await expect(
|
||||
dialog.getByRole('textbox', { name: /share.*url/i })
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('should show stale state with update link button', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const { page } = comfyPage
|
||||
const workflowName = 'share-test-stale'
|
||||
|
||||
await saveAndWait(comfyPage, workflowName)
|
||||
|
||||
const staleRecord: PublishRecord = {
|
||||
...PUBLISHED_RECORD,
|
||||
publish_time: '2020-01-01T00:00:00Z'
|
||||
}
|
||||
|
||||
await mockPublishStatus(page, staleRecord)
|
||||
await mockShareableAssets(page)
|
||||
await openShareDialog(page)
|
||||
|
||||
const dialog = getShareDialog(page)
|
||||
await expect(dialog).toBeVisible()
|
||||
await expect(
|
||||
dialog.getByRole('button', { name: /update\s+link/i })
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('should close dialog when close button is clicked', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const { page } = comfyPage
|
||||
|
||||
await mockPublishStatus(page, null)
|
||||
await mockShareableAssets(page)
|
||||
await openShareDialog(page)
|
||||
|
||||
const dialog = getShareDialog(page)
|
||||
await expect(dialog).toBeVisible()
|
||||
|
||||
await dialog.getByRole('button', { name: /close/i }).click()
|
||||
await expect(dialog).toBeHidden()
|
||||
})
|
||||
|
||||
test('should create link and transition to shared state', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const { page } = comfyPage
|
||||
const workflowName = 'share-test-create'
|
||||
|
||||
await saveAndWait(comfyPage, workflowName)
|
||||
|
||||
await mockPublishStatus(page, null)
|
||||
await mockShareableAssets(page)
|
||||
await mockPublishWorkflow(page, PUBLISHED_RECORD)
|
||||
await openShareDialog(page)
|
||||
|
||||
const dialog = getShareDialog(page)
|
||||
const createButton = dialog.getByRole('button', { name: /create a link/i })
|
||||
await expect(createButton).toBeVisible()
|
||||
await createButton.click()
|
||||
|
||||
await expect(
|
||||
dialog.getByRole('textbox', { name: /share.*url/i })
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('should show tab buttons when comfyHubUploadEnabled is true', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const { page } = comfyPage
|
||||
|
||||
await page.evaluate(() => {
|
||||
const api = window.app!.api
|
||||
api.serverFeatureFlags.value = {
|
||||
...api.serverFeatureFlags.value,
|
||||
comfyhub_upload_enabled: true
|
||||
}
|
||||
})
|
||||
|
||||
await mockPublishStatus(page, null)
|
||||
await mockShareableAssets(page)
|
||||
await openShareDialog(page)
|
||||
|
||||
const dialog = getShareDialog(page)
|
||||
await expect(dialog).toBeVisible()
|
||||
await expect(dialog.getByRole('tab', { name: /share/i })).toBeVisible()
|
||||
await expect(dialog.getByRole('tab', { name: /publish/i })).toBeVisible()
|
||||
})
|
||||
|
||||
test('should switch between share link and publish tabs', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const { page } = comfyPage
|
||||
|
||||
await page.evaluate(() => {
|
||||
const api = window.app!.api
|
||||
api.serverFeatureFlags.value = {
|
||||
...api.serverFeatureFlags.value,
|
||||
comfyhub_upload_enabled: true
|
||||
}
|
||||
})
|
||||
|
||||
await mockPublishStatus(page, null)
|
||||
await mockShareableAssets(page)
|
||||
await openShareDialog(page)
|
||||
|
||||
const dialog = getShareDialog(page)
|
||||
await expect(dialog).toBeVisible()
|
||||
|
||||
await dialog.getByRole('tab', { name: /publish/i }).click()
|
||||
|
||||
const publishPanel = dialog.getByTestId(TestIds.dialogs.publishTabPanel)
|
||||
await expect(publishPanel).toBeVisible()
|
||||
|
||||
await dialog.getByRole('tab', { name: /share/i }).click()
|
||||
await expect(publishPanel).toBeHidden()
|
||||
})
|
||||
|
||||
test('should require acknowledgment before publishing private assets', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const { page } = comfyPage
|
||||
const workflowName = 'share-test-ack'
|
||||
|
||||
await saveAndWait(comfyPage, workflowName)
|
||||
|
||||
await mockPublishStatus(page, null)
|
||||
await mockShareableAssets(page, [PRIVATE_ASSET])
|
||||
await openShareDialog(page)
|
||||
|
||||
const dialog = getShareDialog(page)
|
||||
const createButton = dialog.getByRole('button', { name: /create a link/i })
|
||||
await expect(createButton).toBeDisabled()
|
||||
|
||||
await dialog.getByRole('checkbox').check()
|
||||
await expect(createButton).toBeEnabled()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user