-
Notifications
You must be signed in to change notification settings - Fork 2.6k
added create collection testcase and few import testcases #8402
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,38 +1,69 @@ | ||
| import { test, expect } from '../../../playwright'; | ||
| import { closeAllCollections } from '../../utils/page'; | ||
| import { buildCommonLocators, closeAllCollections } from '../../utils/page'; | ||
|
|
||
| test.describe('GitHub Repository URL Import', () => { | ||
| test.describe('Git repository import', () => { | ||
| test.afterEach(async ({ page }) => { | ||
| await closeAllCollections(page); | ||
| }); | ||
|
|
||
| test('GitHub repository URL import', async ({ page }) => { | ||
| const githubUrl = 'https://github.com/usebruno/github-rest-api-collection'; | ||
| test('TC114: Verify import collection through Cloning from Git Repository', { tag: '@sanity' }, async ({ | ||
| page, | ||
| electronApp, | ||
| createTmpDir | ||
| }) => { | ||
| const gitUrl = 'https://github.com/usebruno/github-rest-api-collection'; | ||
| const collectionName = 'github rest api'; | ||
| const cloneLocation = await createTmpDir('git-clone'); | ||
| const locators = buildCommonLocators(page); | ||
| const importLocators = locators.import; | ||
| const { cloneGit } = importLocators; | ||
|
|
||
| // Test GitHub repository import | ||
| await page.getByTestId('collections-header-add-menu').click(); | ||
| await page.locator('.tippy-box .dropdown-item').filter({ hasText: 'Import collection' }).click(); | ||
| await test.step('Step 01: Go to menu click on the + icon', async () => { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please remove step numbers |
||
| await locators.plusMenu.button().click(); | ||
| await expect(locators.plusMenu.importCollection()).toBeVisible(); | ||
| }); | ||
|
|
||
| // Wait for import collection modal to be ready | ||
| const importModal = page.getByRole('dialog'); | ||
| await importModal.waitFor({ state: 'visible' }); | ||
| await expect(importModal.locator('.bruno-modal-header-title')).toContainText('Import Collection'); | ||
| await test.step('Step 02: Click on Import a collection', async () => { | ||
| await locators.plusMenu.importCollection().click(); | ||
|
|
||
| // Select the GitHub tab | ||
| await page.getByTestId('github-tab').click(); | ||
| await importLocators.modal().waitFor({ state: 'visible' }); | ||
| await expect(importLocators.modalTitle()).toContainText('Import Collection'); | ||
| await expect(importLocators.fileTab()).toBeVisible(); | ||
| await expect(importLocators.gitRepositoryTab()).toBeVisible(); | ||
| await expect(importLocators.urlTab()).toBeVisible(); | ||
| }); | ||
|
|
||
| // Fill in the URL input | ||
| await page.getByTestId('git-url-input').fill(githubUrl); | ||
| await page.locator('#clone-git-button').click(); | ||
| await test.step('Step 03: Go to git repository section and Enter the URL and select the location to save the repo then click on the import button', async () => { | ||
| await importLocators.gitRepositoryTab().click(); | ||
| await importLocators.gitUrlInput().fill(gitUrl); | ||
| await importLocators.cloneGitButton().click(); | ||
| await importLocators.loader().waitFor({ state: 'hidden' }); | ||
|
|
||
| // Wait for the loader to disappear | ||
| await page.locator('#import-collection-loader').waitFor({ state: 'hidden' }); | ||
| await expect(cloneGit.modal()).toBeVisible(); | ||
| await expect(cloneGit.modal()).toContainText(gitUrl); | ||
|
|
||
| // Verify that the Clone Git Repository modal is displayed | ||
| const cloneModal = page.getByRole('dialog'); | ||
| await expect(cloneModal.locator('.bruno-modal-header-title')).toContainText('Clone Git Repository'); | ||
| await electronApp.evaluate(({ dialog }, dir) => { | ||
| dialog.showOpenDialog = async () => ({ | ||
| canceled: false, | ||
| filePaths: [dir] | ||
| }); | ||
| }, cloneLocation); | ||
|
Comment on lines
+45
to
+50
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift Restore the Electron dialog stub before the test exits. This overwrites As per coding guidelines, 🤖 Prompt for AI AgentsSource: Coding guidelines |
||
|
|
||
| // Cleanup: close any open modals using Cancel button (avoids form validation) | ||
| await page.getByRole('button', { name: 'Cancel' }).click(); | ||
| await cloneGit.locationInput().click(); | ||
| await expect(cloneGit.locationInput()).toHaveValue(cloneLocation); | ||
|
|
||
| await cloneGit.cloneButton().click(); | ||
| await expect(cloneGit.collectionItemTitle(collectionName)).toBeVisible(); | ||
| }); | ||
|
|
||
| await test.step('Step 04: Select the desired collections and click on open', async () => { | ||
| await cloneGit.collectionCheckbox(collectionName).check(); | ||
|
|
||
| await cloneGit.openButton().click(); | ||
| await cloneGit.modal().waitFor({ state: 'hidden' }); | ||
|
|
||
| await expect(locators.sidebar.collection(collectionName)).toBeVisible(); | ||
| await expect(locators.toast.repositoryClonedSuccessfully()).toBeVisible(); | ||
| }); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,45 +1,60 @@ | ||
| import { test, expect } from '../../../playwright'; | ||
| import { closeAllCollections, openCollection } from '../../utils/page'; | ||
| import { buildCommonLocators, closeAllCollections, openCollection } from '../../utils/page'; | ||
|
|
||
| test.describe('Insomnia URL Import', () => { | ||
| test.afterEach(async ({ page }) => { | ||
| // cleanup: close all collections | ||
| await closeAllCollections(page); | ||
| }); | ||
|
|
||
| test('Insomnia URL import', async ({ page, createTmpDir }) => { | ||
| const insomniaUrl = 'https://raw.githubusercontent.com/usebruno/bruno/refs/heads/main/tests/import/insomnia/fixtures/insomnia-v5.yaml'; | ||
|
|
||
| await page.getByTestId('collections-header-add-menu').click(); | ||
| await page.locator('.tippy-box .dropdown-item').filter({ hasText: 'Import collection' }).click(); | ||
|
|
||
| // Wait for import collection modal to be ready | ||
| const importModal = page.getByTestId('import-collection-modal'); | ||
| await importModal.waitFor({ state: 'visible' }); | ||
|
|
||
| await page.getByTestId('url-tab').click(); | ||
| await page.getByTestId('url-input').waitFor({ state: 'visible' }); | ||
| await page.getByTestId('url-input').fill(insomniaUrl); | ||
| await page.locator('#import-url-button').click(); | ||
|
|
||
| // Wait for the loader to disappear | ||
| await page.locator('#import-collection-loader').waitFor({ state: 'hidden' }); | ||
|
|
||
| // Verify that the collection location modal appears | ||
| const locationModal = page.getByTestId('import-collection-location-modal'); | ||
| await expect(locationModal.getByText('Test API Collection v5')).toBeVisible(); | ||
|
|
||
| // Select a location and import | ||
| await page.locator('#collection-location').fill(await createTmpDir('test-api-collection-v5')); | ||
| await locationModal.getByRole('button', { name: 'Import' }).click(); | ||
| await locationModal.waitFor({ state: 'hidden' }); | ||
|
|
||
| // Verify the collection was imported successfully and configure it | ||
| await expect(page.locator('#sidebar-collection-name').getByText('Test API Collection v5')).toBeVisible(); | ||
| await openCollection(page, 'Test API Collection v5'); | ||
|
|
||
| // Verify these folder names are present | ||
| await expect(page.locator('.collection-item-name').getByText('API Tests')).toBeVisible(); | ||
| await expect(page.locator('.collection-item-name').getByText('Data Management')).toBeVisible(); | ||
| }); | ||
| test('TC813: Verify Import collection from Valid Insomnia Export from Direct URL', | ||
| { tag: '@sanity' }, | ||
| async ({ page, createTmpDir }) => { | ||
| const insomniaUrl | ||
| = 'https://raw.githubusercontent.com/usebruno/bruno/refs/heads/main/tests/import/insomnia/fixtures/insomnia-v5.yaml'; | ||
| const collectionName = 'Test API Collection v5'; | ||
| const collectionLocation = await createTmpDir('test-api-collection-v5'); | ||
| const locators = buildCommonLocators(page); | ||
| const importLocators = locators.import; | ||
|
|
||
| await test.step('Step 01: Navigate to the Import functionality in Bruno', async () => { | ||
| await locators.plusMenu.button().click(); | ||
| await expect(locators.plusMenu.importCollection()).toBeVisible(); | ||
| await locators.plusMenu.importCollection().click(); | ||
|
|
||
| await importLocators.modal().waitFor({ state: 'visible' }); | ||
| await expect(importLocators.modalTitle()).toContainText('Import Collection'); | ||
| await expect(importLocators.fileTab()).toBeVisible(); | ||
| await expect(importLocators.gitRepositoryTab()).toBeVisible(); | ||
| await expect(importLocators.urlTab()).toBeVisible(); | ||
| }); | ||
|
|
||
| await test.step('Step 02: Select \'Import from URL\' option', async () => { | ||
| await importLocators.urlTab().click(); | ||
| await expect(importLocators.urlInput()).toBeVisible(); | ||
| await expect(importLocators.importUrlButton()).toBeVisible(); | ||
| }); | ||
|
|
||
| await test.step('Step 03: Enter a valid Insomnia export URL', async () => { | ||
| await importLocators.urlInput().fill(insomniaUrl); | ||
| await expect(importLocators.urlInput()).toHaveValue(insomniaUrl); | ||
| }); | ||
|
|
||
| await test.step('Step 04: Initiate the import process', async () => { | ||
| await importLocators.importUrlButton().click(); | ||
| await importLocators.loader().waitFor({ state: 'hidden' }); | ||
| await expect(importLocators.locationModal()).toBeVisible(); | ||
| await expect(importLocators.locationModal().getByText(collectionName)).toBeVisible(); | ||
| }); | ||
|
|
||
| await test.step('Step 05: Verify successful import of the Insomnia export', async () => { | ||
| await importLocators.locationInput().fill(collectionLocation); | ||
| await importLocators.importButton(importLocators.locationModal()).click(); | ||
| await importLocators.locationModal().waitFor({ state: 'hidden' }); | ||
| await expect(locators.sidebar.collection(collectionName)).toBeVisible(); | ||
| await openCollection(page, collectionName); | ||
| await expect(page.locator('.collection-item-name').getByText('API Tests')).toBeVisible(); | ||
| await expect(page.locator('.collection-item-name').getByText('Data Management')).toBeVisible(); | ||
| }); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -67,6 +67,11 @@ export const buildCommonLocators = (page: Page) => ({ | |||||||||||||||||||||||||||||||||
| submitButton: () => page.locator('.bruno-modal-footer .submit'), | ||||||||||||||||||||||||||||||||||
| newRequestMethodOption: (id: string) => page.getByTestId(`method-selector-${id.toLowerCase()}`) | ||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||
| toast: { | ||||||||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move toast locators to own file. Add generic method like for success, error, custom etc, no need to create methods for every messages. |
||||||||||||||||||||||||||||||||||
| collectionCreated: () => page.getByText('Collection created!'), | ||||||||||||||||||||||||||||||||||
| repositoryClonedSuccessfully: () => page.getByText('Repository cloned successfully'), | ||||||||||||||||||||||||||||||||||
| collectionImportedSuccessfully: () => page.getByText('Collection imported successfully') | ||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||
| environment: { | ||||||||||||||||||||||||||||||||||
| selector: () => page.getByTestId('environment-selector-trigger'), | ||||||||||||||||||||||||||||||||||
| collectionTab: () => page.getByTestId('env-tab-collection'), | ||||||||||||||||||||||||||||||||||
|
|
@@ -237,13 +242,34 @@ export const buildCommonLocators = (page: Page) => ({ | |||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||
| import: { | ||||||||||||||||||||||||||||||||||
| modal: () => page.locator('[data-testid="import-collection-modal"]'), | ||||||||||||||||||||||||||||||||||
| modalTitle: () => page.locator('[data-testid="import-collection-modal"] .bruno-modal-header-title'), | ||||||||||||||||||||||||||||||||||
| fileTab: () => page.getByTestId('file-tab'), | ||||||||||||||||||||||||||||||||||
| gitRepositoryTab: () => page.getByTestId('github-tab'), | ||||||||||||||||||||||||||||||||||
| urlTab: () => page.getByTestId('url-tab'), | ||||||||||||||||||||||||||||||||||
| gitUrlInput: () => page.getByTestId('git-url-input'), | ||||||||||||||||||||||||||||||||||
| urlInput: () => page.getByTestId('url-input'), | ||||||||||||||||||||||||||||||||||
| cloneGitButton: () => page.locator('#clone-git-button'), | ||||||||||||||||||||||||||||||||||
| importUrlButton: () => page.locator('#import-url-button'), | ||||||||||||||||||||||||||||||||||
| loader: () => page.locator('#import-collection-loader'), | ||||||||||||||||||||||||||||||||||
| locationModal: () => page.locator('[data-testid="import-collection-location-modal"]'), | ||||||||||||||||||||||||||||||||||
| locationInput: () => page.locator('#collection-location'), | ||||||||||||||||||||||||||||||||||
| fileInput: () => page.locator('input[type="file"]'), | ||||||||||||||||||||||||||||||||||
| envOption: (name: string) => page.locator('.dropdown-item').getByText(name, { exact: true }), | ||||||||||||||||||||||||||||||||||
| parsingError: () => page.getByTestId('import-error-message'), | ||||||||||||||||||||||||||||||||||
| browseLink: (root?: Locator) => (root ?? page).getByTestId('import-collection-browse-link'), | ||||||||||||||||||||||||||||||||||
| importButton: (root?: Locator) => (root ?? page).getByTestId('import-collection-location-modal-submit-btn'), | ||||||||||||||||||||||||||||||||||
| cloneGit: { | ||||||||||||||||||||||||||||||||||
| modal: () => page.locator('.bruno-modal-card').filter({ hasText: 'Clone Git Repository' }), | ||||||||||||||||||||||||||||||||||
| locationInput: () => | ||||||||||||||||||||||||||||||||||
| page.locator('.bruno-modal-card').filter({ hasText: 'Clone Git Repository' }).locator('#collection-location'), | ||||||||||||||||||||||||||||||||||
| cloneButton: () => | ||||||||||||||||||||||||||||||||||
| page.locator('.bruno-modal-card').filter({ hasText: 'Clone Git Repository' }).getByRole('button', { name: 'Clone', exact: true }), | ||||||||||||||||||||||||||||||||||
| openButton: () => | ||||||||||||||||||||||||||||||||||
| page.locator('.bruno-modal-card').filter({ hasText: 'Clone Git Repository' }).getByRole('button', { name: 'Open', exact: true }), | ||||||||||||||||||||||||||||||||||
| collectionItemTitle: (name: string) => page.locator('.selection-item-title').filter({ hasText: name }), | ||||||||||||||||||||||||||||||||||
| collectionCheckbox: (name: string) => | ||||||||||||||||||||||||||||||||||
| page.locator('.selection-item').filter({ hasText: name }).locator('input[type="checkbox"]') | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+269
to
+271
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win Scope the collection-selection locators to the clone modal. These two locators search the whole page and use partial text matching, so another Proposed fix- collectionItemTitle: (name: string) => page.locator('.selection-item-title').filter({ hasText: name }),
- collectionCheckbox: (name: string) =>
- page.locator('.selection-item').filter({ hasText: name }).locator('input[type="checkbox"]')
+ collectionItemTitle: (name: string) =>
+ page
+ .locator('.bruno-modal-card')
+ .filter({ hasText: 'Clone Git Repository' })
+ .locator('.selection-item-title')
+ .getByText(name, { exact: true }),
+ collectionCheckbox: (name: string) =>
+ page
+ .locator('.bruno-modal-card')
+ .filter({ hasText: 'Clone Git Repository' })
+ .locator('.selection-item')
+ .filter({ has: page.getByText(name, { exact: true }) })
+ .locator('input[type="checkbox"]')📝 Committable suggestion
Suggested change
🤖 Prompt for AI AgentsSource: Path instructions |
||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||
| ...(() => { | ||||||||||||||||||||||||||||||||||
| const issuesToast = () => page.getByTestId('import-issues-toast').last(); | ||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Make the collection name unique per test instance.
Line 57 hardcodes
'test-collection', but the cleanup here only closes collections. That leaves this spec vulnerable to stale app state: an existing collection with the same name can satisfy the sidebar assertion, or reruns/workers can trip duplicate-name handling. Please derive the visible collection name from isolated per-test state instead of a fixed literal. As per coding guidelines, "E2E tests must be parallel-safe. No shared user data directories... Each test gets isolated temp paths and unique workspace/project names." As per path instructions, "Each test gets isolated temp paths and unique workspace/project names."🤖 Prompt for AI Agents
Sources: Coding guidelines, Path instructions