mirror of
https://github.com/Kong/insomnia.git
synced 2026-08-04 11:52:33 -04:00
backport: manual resolution required for #9878
This commit is contained in:
25
package-lock.json
generated
25
package-lock.json
generated
@@ -17363,6 +17363,27 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
"node_modules/git-http-backend": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/git-http-backend/-/git-http-backend-1.1.2.tgz",
|
||||
"integrity": "sha512-Gx7n/kyCEXGFZlCGmbsEsyeyabLs8XWeb+E/6842up7p3PktQS2/8rlNfB6hCagnW0pJ13Tn8E3yhOkKS6ihdg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"git-side-band-message": "~0.0.3",
|
||||
"inherits": "~2.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/git-side-band-message": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/git-side-band-message/-/git-side-band-message-0.0.3.tgz",
|
||||
"integrity": "sha512-4Rq4xm1+zqCkmuHxRbGdA5ActF7F4UfgK8uI0B7ZfSkByZfikRuF7mqHlvqmycvqos7jpXNkgsZK7DThLLHG3w==",
|
||||
"dev": true,
|
||||
"license": "BSD"
|
||||
},
|
||||
>>>>>>> 7809d458a (Update E2E test for git sync [INS-2258] (#9878))
|
||||
"node_modules/glob": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||
@@ -29421,6 +29442,10 @@
|
||||
"esbuild-runner": "2.2.2",
|
||||
"express": "^4.21.2",
|
||||
"express-basic-auth": "^1.2.1",
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
"git-http-backend": "^1.1.2",
|
||||
>>>>>>> 7809d458a (Update E2E test for git sync [INS-2258] (#9878))
|
||||
"graphql": "^16.10.0",
|
||||
"graphql-http": "^1.22.4",
|
||||
"http-errors": "^2.0.0",
|
||||
|
||||
@@ -30,6 +30,10 @@
|
||||
"esbuild-runner": "2.2.2",
|
||||
"express": "^4.21.2",
|
||||
"express-basic-auth": "^1.2.1",
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
"git-http-backend": "^1.1.2",
|
||||
>>>>>>> 7809d458a (Update E2E test for git sync [INS-2258] (#9878))
|
||||
"graphql": "^16.10.0",
|
||||
"graphql-http": "^1.22.4",
|
||||
"http-errors": "^2.0.0",
|
||||
|
||||
@@ -1,4 +1,34 @@
|
||||
import type { PlaywrightTestConfig } from '@playwright/test';
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
const isWindows = os.platform() === 'win32';
|
||||
const echoServer: PlaywrightTestConfig['webServer'] = {
|
||||
name: 'Echo server',
|
||||
command: 'npm run serve',
|
||||
url: 'http://localhost:4010',
|
||||
timeout: 15 * 1000,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
stdout: 'pipe',
|
||||
stderr: 'pipe',
|
||||
wait: {
|
||||
stdout: /Listening at http/,
|
||||
},
|
||||
};
|
||||
const viteServer: PlaywrightTestConfig['webServer'] = {
|
||||
name: 'Vite Server',
|
||||
cwd: '../../',
|
||||
command: 'npm run watch:app',
|
||||
url: 'http://localhost:3334',
|
||||
timeout: 120 * 1000,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
stdout: 'pipe',
|
||||
stderr: 'pipe',
|
||||
wait: {
|
||||
stdout: /VITE\s+ready in/,
|
||||
},
|
||||
};
|
||||
const onlyStartWebServerInDev = !process.env.BUNDLE || process.env.BUNDLE === 'dev';
|
||||
>>>>>>> 7809d458a (Update E2E test for git sync [INS-2258] (#9878))
|
||||
const config: PlaywrightTestConfig = {
|
||||
projects: [
|
||||
{
|
||||
|
||||
@@ -1,13 +1,28 @@
|
||||
import { spawn } from 'node:child_process';
|
||||
import crypto from 'node:crypto';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { cpSync, existsSync, mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
||||
import { createServer } from 'node:https';
|
||||
import { tmpdir } from 'node:os';
|
||||
import nodePath from 'node:path';
|
||||
import type { Duplex } from 'node:stream';
|
||||
|
||||
import * as bodyParser from 'body-parser';
|
||||
import cookieParser from 'cookie-parser';
|
||||
import express from 'express';
|
||||
import { createHandler } from 'graphql-http/lib/use/http';
|
||||
|
||||
interface GitBackendService {
|
||||
type: string;
|
||||
cmd: string;
|
||||
args: string[];
|
||||
createStream(): Duplex;
|
||||
}
|
||||
// git-http-backend has no @types package; require+cast is the standard workaround
|
||||
const backend = require('git-http-backend') as (
|
||||
url: string,
|
||||
cb: (err: Error | null, service: GitBackendService) => void,
|
||||
) => NodeJS.ReadWriteStream;
|
||||
|
||||
import { basicAuthRouter } from './basic-auth';
|
||||
import cloudSyncApi from './cloud-sync-api';
|
||||
import githubApi from './github-api';
|
||||
@@ -152,6 +167,54 @@ app.get('/v1/oauth/azure/config', (_req, res) => {
|
||||
});
|
||||
});
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
const GIT_FIXTURE_ROOT = nodePath.join(__dirname, '../fixtures/git-repo');
|
||||
let currentGitTmpDir: string | null = null;
|
||||
|
||||
// Create a fresh per-test copy of the git fixture repo
|
||||
app.post('/v1/test-utils/git/setup', (_req, res) => {
|
||||
if (currentGitTmpDir && existsSync(currentGitTmpDir)) {
|
||||
rmSync(currentGitTmpDir, { recursive: true, force: true });
|
||||
}
|
||||
currentGitTmpDir = mkdtempSync(nodePath.join(tmpdir(), 'insomnia-git-'));
|
||||
cpSync(GIT_FIXTURE_ROOT, currentGitTmpDir, { recursive: true });
|
||||
res.json({ success: true });
|
||||
});
|
||||
|
||||
// Remove the per-test copy
|
||||
app.delete('/v1/test-utils/git/setup', (_req, res) => {
|
||||
if (currentGitTmpDir && existsSync(currentGitTmpDir)) {
|
||||
rmSync(currentGitTmpDir, { recursive: true, force: true });
|
||||
}
|
||||
currentGitTmpDir = null;
|
||||
res.json({ success: true });
|
||||
});
|
||||
|
||||
// Git smart HTTP server backed by git-http-backend — accepts real pushes.
|
||||
// Falls back to the fixture root if no per-test dir is set up.
|
||||
app.use('/git', (req, res) => {
|
||||
const root = currentGitTmpDir ?? GIT_FIXTURE_ROOT;
|
||||
// req.url has the '/git' prefix stripped by Express, e.g. '/git-server.git/info/refs?...'
|
||||
const repoPath = nodePath.join(root, req.url.split('?')[0].split('/')[1]);
|
||||
|
||||
req.pipe(
|
||||
backend(req.url, (err, service) => {
|
||||
if (err) {
|
||||
res.status(500).end(err.message);
|
||||
return;
|
||||
}
|
||||
res.setHeader('content-type', service.type);
|
||||
const ps = spawn(service.cmd, service.args.concat(repoPath), {
|
||||
env: { ...process.env, GIT_HTTP_EXPORT_ALL: '1' },
|
||||
});
|
||||
ps.stderr.on('data', d => console.error('[git]', String(d)));
|
||||
ps.stdout.pipe(service.createStream()).pipe(ps.stdin);
|
||||
}),
|
||||
).pipe(res);
|
||||
});
|
||||
|
||||
>>>>>>> 7809d458a (Update E2E test for git sync [INS-2258] (#9878))
|
||||
startWebSocketServer(
|
||||
app.listen(port, '::', () => {
|
||||
console.log(`Listening at http://localhost:${port}`);
|
||||
|
||||
@@ -28,6 +28,7 @@ test.describe('Git Sync', () => {
|
||||
});
|
||||
});
|
||||
|
||||
<<<<<<< HEAD
|
||||
test.afterEach(async ({ request }) => {
|
||||
// Re-enable git sync feature flag for organization
|
||||
await request.post('http://127.0.0.1:4010/v1/test-utils/organizations/features', {
|
||||
@@ -97,5 +98,160 @@ test.describe('Git Sync', () => {
|
||||
await expect.soft(banner).not.toHaveText('Git Sync');
|
||||
await expect.soft(page.getByLabel('Project Type: git')).toBeDisabled();
|
||||
});
|
||||
=======
|
||||
test.beforeEach(async ({ insomnia, request }) => {
|
||||
await request.post('http://127.0.0.1:4010/v1/test-utils/git/setup');
|
||||
await addAccessTokenGitCredential(insomnia);
|
||||
await insomnia.projectPage.createGitSyncProject();
|
||||
});
|
||||
|
||||
test.afterEach(async ({ request }) => {
|
||||
await request.delete('http://127.0.0.1:4010/v1/test-utils/git/setup');
|
||||
});
|
||||
|
||||
// Creates a git sync project, opens the Branches modal, creates "branch1",
|
||||
// and verifies the active branch switches to branch1.
|
||||
test('Create new branch and switch to it', async ({ page }) => {
|
||||
await page.getByTestId('git-dropdown').click();
|
||||
await page.getByRole('menuitemradio', { name: 'Branches' }).click();
|
||||
await page.getByRole('textbox', { name: 'New branch name:' }).click();
|
||||
await page.getByRole('textbox', { name: 'New branch name:' }).fill('branch1');
|
||||
await page.getByRole('button', { name: 'Create', exact: true }).click();
|
||||
await expect.soft(page.getByText('branch1 *')).toBeVisible();
|
||||
});
|
||||
|
||||
// Creates a collection to produce an unstaged change, stages it, commits with message "1",
|
||||
// then opens History and verifies the commit appears in the log.
|
||||
test('Commit and check history', async ({ page }) => {
|
||||
await page.getByRole('button', { name: 'New request collection' }).click();
|
||||
await page.getByRole('textbox', { name: 'Name', exact: true }).click();
|
||||
await page.getByRole('textbox', { name: 'Name', exact: true }).press('ControlOrMeta+a');
|
||||
await page.getByRole('textbox', { name: 'Name', exact: true }).fill('Collection 1');
|
||||
await page.getByRole('textbox', { name: 'File name my_collection' }).click();
|
||||
await page.getByRole('textbox', { name: 'File name my_collection' }).press('ControlOrMeta+a');
|
||||
await page.getByRole('textbox', { name: 'File name my_collection' }).fill('collection_1');
|
||||
await page.getByRole('button', { name: 'Create', exact: true }).click();
|
||||
await page.getByTestId('git-dropdown').click();
|
||||
await expect.soft(page.getByRole('menuitemradio', { name: 'Commit' })).toBeVisible();
|
||||
await page.getByRole('menuitemradio', { name: 'Commit' }).click();
|
||||
await expect.soft(page.getByLabel('Unstaged changes').locator('span')).toContainText('collection_1.yaml');
|
||||
|
||||
await page.locator('button[name="Stage all changes"]').click();
|
||||
await page.getByRole('textbox', { name: 'Message' }).click();
|
||||
await page.getByRole('textbox', { name: 'Message' }).fill('1');
|
||||
await page.getByRole('button', { name: 'Commit', exact: true }).click();
|
||||
await page.getByTestId('git-dropdown').click();
|
||||
await page.getByText('History').click();
|
||||
await expect.soft(page.getByLabel('1', { exact: true }).getByRole('rowheader')).toContainText('1');
|
||||
});
|
||||
|
||||
// Creates branch1, commits a new collection on it, switches back to master,
|
||||
// merges branch1 into master, and verifies the collection is visible on master.
|
||||
test('Merge branch and verify changes on the other branch has been merged into current branch', async ({ page }) => {
|
||||
await page.getByTestId('git-dropdown').click();
|
||||
await page.getByRole('menuitemradio', { name: 'Branches' }).click();
|
||||
await page.getByRole('textbox', { name: 'New branch name:' }).click();
|
||||
await page.getByRole('textbox', { name: 'New branch name:' }).fill('branch1');
|
||||
await page.getByRole('button', { name: 'Create', exact: true }).click();
|
||||
await expect.soft(page.getByText('branch1 *')).toBeVisible();
|
||||
await page.getByTestId('close-git-project-branches-modal').click();
|
||||
await page.getByTestId('git-project-branches-modal-overlay').waitFor({ state: 'hidden' });
|
||||
await page.getByRole('button', { name: 'New request collection' }).click();
|
||||
await page.getByRole('textbox', { name: 'Name', exact: true }).click();
|
||||
await page.getByRole('textbox', { name: 'Name', exact: true }).press('ControlOrMeta+a');
|
||||
await page.getByRole('textbox', { name: 'Name', exact: true }).fill('collection 1');
|
||||
await page.getByRole('textbox', { name: 'File name my_collection' }).click();
|
||||
await page.getByRole('textbox', { name: 'File name my_collection' }).press('ControlOrMeta+a');
|
||||
await page.getByRole('textbox', { name: 'File name my_collection' }).fill('collection_1');
|
||||
await page.getByRole('button', { name: 'Create', exact: true }).click();
|
||||
await page.getByTestId('project').click();
|
||||
await page.getByTestId('git-dropdown').click();
|
||||
await page.getByRole('menuitemradio', { name: 'Commit' }).click();
|
||||
await page.locator('button[name="Stage all changes"]').click();
|
||||
await page.getByRole('textbox', { name: 'Message' }).click();
|
||||
await page.getByRole('textbox', { name: 'Message' }).fill('commit 1');
|
||||
await page.getByRole('button', { name: 'Commit', exact: true }).click();
|
||||
await page.getByTestId('git-dropdown').click();
|
||||
await page.getByRole('menuitemradio', { name: 'master' }).click();
|
||||
await page.locator('html').click();
|
||||
await page.getByTestId('git-dropdown').click();
|
||||
await page.getByRole('menuitemradio', { name: 'Branches' }).click();
|
||||
await page.getByLabel('branch1').getByRole('button', { name: 'Merge' }).click();
|
||||
await page.getByRole('button', { name: ' Confirm' }).click();
|
||||
await page.getByTestId('close-git-project-branches-modal').click();
|
||||
await page.getByTestId('git-project-branches-modal-overlay').waitFor({ state: 'hidden' });
|
||||
await expect.soft(page.getByText('collection 1')).toBeVisible();
|
||||
>>>>>>> 7809d458a (Update E2E test for git sync [INS-2258] (#9878))
|
||||
});
|
||||
|
||||
// Creates a collection, commits it, then pushes to the remote git server.
|
||||
// Verifies the "Push completed" toast appears, confirming a successful push.
|
||||
test('Push committed changes to remote', async ({ page }) => {
|
||||
await page.getByRole('button', { name: 'New request collection' }).click();
|
||||
await page.getByRole('textbox', { name: 'Name', exact: true }).click();
|
||||
await page.getByRole('textbox', { name: 'Name', exact: true }).press('ControlOrMeta+a');
|
||||
await page.getByRole('textbox', { name: 'Name', exact: true }).fill('Push Test Collection');
|
||||
await page.getByRole('textbox', { name: 'File name my_collection' }).click();
|
||||
await page.getByRole('textbox', { name: 'File name my_collection' }).press('ControlOrMeta+a');
|
||||
await page.getByRole('textbox', { name: 'File name my_collection' }).fill('push_test_collection');
|
||||
await page.getByRole('button', { name: 'Create', exact: true }).click();
|
||||
|
||||
await page.getByTestId('git-dropdown').click();
|
||||
await page.getByRole('menuitemradio', { name: 'Commit' }).click();
|
||||
await page.locator('button[name="Stage all changes"]').click();
|
||||
await page.getByRole('textbox', { name: 'Message' }).fill('push test commit');
|
||||
await page.getByRole('button', { name: 'Commit', exact: true }).click();
|
||||
|
||||
await page.getByTestId('git-dropdown').click();
|
||||
await page.getByRole('menuitemradio', { name: 'Push' }).click();
|
||||
|
||||
await expect.soft(page.getByText('Push completed')).toBeVisible();
|
||||
});
|
||||
|
||||
// Creates "branch-to-delete", checks out master, then deletes the branch via the
|
||||
// two-step PromptButton (Delete → Confirm). Verifies the branch is removed from the list.
|
||||
test('Delete a branch', async ({ page }) => {
|
||||
await page.getByTestId('git-dropdown').click();
|
||||
await page.getByRole('menuitemradio', { name: 'Branches' }).click();
|
||||
await page.getByRole('textbox', { name: 'New branch name:' }).click();
|
||||
await page.getByRole('textbox', { name: 'New branch name:' }).fill('branch-to-delete');
|
||||
await page.getByRole('button', { name: 'Create', exact: true }).click();
|
||||
await expect.soft(page.getByText('branch-to-delete *')).toBeVisible();
|
||||
|
||||
await page.getByRole('row', { name: 'master' }).getByRole('button', { name: 'Checkout' }).click();
|
||||
await expect.soft(page.getByText('master *')).toBeVisible();
|
||||
|
||||
await page.getByRole('row', { name: 'branch-to-delete' }).getByRole('button', { name: 'Delete' }).click();
|
||||
await page.getByRole('row', { name: 'branch-to-delete' }).getByRole('button', { name: 'Confirm' }).click();
|
||||
|
||||
await expect.soft(page.getByRole('row', { name: 'branch-to-delete' })).toBeHidden();
|
||||
|
||||
await page.getByTestId('close-git-project-branches-modal').click();
|
||||
await page.getByTestId('git-project-branches-modal-overlay').waitFor({ state: 'hidden' });
|
||||
await expect.soft(page.getByTestId('git-dropdown')).toContainText('master');
|
||||
});
|
||||
|
||||
// Creates a collection to produce an unstaged change, opens the staging modal,
|
||||
// clicks "Discard all changes" and confirms. Verifies the modal auto-closes,
|
||||
// indicating all changes were discarded.
|
||||
test('Discard all unstaged changes', async ({ page }) => {
|
||||
await page.getByRole('button', { name: 'New request collection' }).click();
|
||||
await page.getByRole('textbox', { name: 'Name', exact: true }).click();
|
||||
await page.getByRole('textbox', { name: 'Name', exact: true }).press('ControlOrMeta+a');
|
||||
await page.getByRole('textbox', { name: 'Name', exact: true }).fill('Discard Test Collection');
|
||||
await page.getByRole('textbox', { name: 'File name my_collection' }).click();
|
||||
await page.getByRole('textbox', { name: 'File name my_collection' }).press('ControlOrMeta+a');
|
||||
await page.getByRole('textbox', { name: 'File name my_collection' }).fill('discard_test_collection');
|
||||
await page.getByRole('button', { name: 'Create', exact: true }).click();
|
||||
|
||||
await page.getByTestId('git-dropdown').click();
|
||||
await page.getByRole('menuitemradio', { name: 'Commit' }).click();
|
||||
await expect.soft(page.getByLabel('Unstaged changes').locator('span')).toContainText('discard_test_collection.yaml');
|
||||
|
||||
await page.locator('button[name="Discard all changes"]').click();
|
||||
await page.getByTestId('discard-changes-confirm-button').click();
|
||||
|
||||
// After discarding all changes the staging modal auto-closes
|
||||
await expect.soft(page.getByLabel('Unstaged changes')).toBeHidden();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1619,6 +1619,7 @@ const ConfirmDiscardModal = ({ message, onConfirm, onClose }: ConfirmModalProps)
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
data-testid="discard-changes-confirm-button"
|
||||
className="flex h-full items-center justify-center gap-2 rounded-md border border-solid border-(--hl-md) bg-(--color-surprise) px-4 py-2 text-sm font-semibold text-(--color-font-surprise) ring-1 ring-transparent transition-all hover:bg-(--color-surprise)/80 focus:ring-(--hl-md) focus:ring-inset aria-pressed:opacity-80"
|
||||
onPress={() => {
|
||||
if (typeof onConfirm === 'function') {
|
||||
|
||||
Reference in New Issue
Block a user