Files
jackwener__opencli/clis/chatgpt/utils.test.js
Benjamin Liu 25e86532a3 fix(chatgpt): fix image generation detection and output path (#1218)
* fix(chatgpt): fix image generation detection and output path

Three fixes for chatgpt image command:

1. Page navigation: ChatGPT redirects away from the conversation
   after sending. Poll for the /c/ URL after send, then periodically
   reload the conversation page during image wait to pick up
   asynchronously rendered images.

2. Composer selector: add fallback selectors for the chat input
   since ChatGPT uses different aria-labels across UI versions.

3. Output path: the default '~/Pictures/chatgpt' was passed as a
   literal string without tilde expansion, creating a directory
   named '~' in the working directory. Removed the string default
   and use os.homedir() fallback instead.

Fixes #1206

* fix(chatgpt): fail fast on image export failures

* fix(chatgpt): avoid reloads during image generation

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-05-01 20:01:34 +08:00

64 lines
2.5 KiB
JavaScript

import { describe, expect, it, vi } from 'vitest';
import { __test__, waitForChatGPTImages } from './utils.js';
function createPageMock({ location = '', generating = [], imageUrls = [] } = {}) {
let generatingIndex = 0;
let imageIndex = 0;
return {
wait: vi.fn().mockResolvedValue(undefined),
goto: vi.fn().mockResolvedValue(undefined),
evaluate: vi.fn((script) => {
if (script === 'window.location.href') return Promise.resolve(location);
if (script.includes('Stop generating') || script.includes('Thinking')) {
const value = generating[Math.min(generatingIndex, generating.length - 1)] ?? false;
generatingIndex += 1;
return Promise.resolve(value);
}
if (script.includes("document.querySelectorAll('img')")) {
const value = imageUrls[Math.min(imageIndex, imageUrls.length - 1)] ?? [];
imageIndex += 1;
return Promise.resolve(value);
}
return Promise.resolve(undefined);
}),
};
}
describe('chatgpt image wait contract', () => {
it('does not periodically reload the conversation while generation is still active', async () => {
const convUrl = 'https://chatgpt.com/c/demo';
const page = createPageMock({
location: convUrl,
generating: [true, true, true, true, true, true],
});
await expect(waitForChatGPTImages(page, [], 18, convUrl)).resolves.toEqual([]);
expect(page.goto).not.toHaveBeenCalled();
});
it('jumps back to the captured conversation when the page drifts away', async () => {
const convUrl = 'https://chatgpt.com/c/demo';
const page = createPageMock({
location: 'https://chatgpt.com/',
generating: [false],
imageUrls: [['https://cdn.openai.com/generated/demo.png']],
});
await expect(waitForChatGPTImages(page, [], 3, convUrl)).resolves.toEqual([
'https://cdn.openai.com/generated/demo.png',
]);
expect(page.goto).toHaveBeenCalledWith(convUrl);
});
it('treats query and hash variants as the same conversation', () => {
expect(__test__.isSameChatGPTConversation(
'https://chatgpt.com/c/demo?model=gpt-image-1',
'https://chatgpt.com/c/demo',
)).toBe(true);
expect(__test__.isSameChatGPTConversation(
'https://chatgpt.com/c/other',
'https://chatgpt.com/c/demo',
)).toBe(false);
});
});