Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@
let originalTestContent = "";
let currentPrompt = "";
let originalLineCount = 0;
let temporaryDisplayContent = "";
let contentAddedDuringGeneration = "";

// Persistent highlighting variables
let observer: MutationObserver | null = null;
Expand All @@ -85,35 +87,175 @@
const updateBeautifiedState = (val: boolean): void => {
isBodyBeautified = val;
};

//handler function to remove the changes on tab switch
export const handleTabChange = () => {
if (showGeneratedTestActions) {
rejectGeneratedTest();
}
};

const handleCodeMirrorChange = (e: any) => {
onTestsChange({ ...tests, script: e.detail });
const newContent = e.detail;

// If we have generated test actions showing, protect the generated content
if (showGeneratedTestActions) {
const newLines = newContent.split("\n");

// Check if lines in the generated range have been modified
let generatedContentModified = false;
const originalGeneratedLines = generatedTestContent.split("\n");

for (let i = 0; i < originalGeneratedLines.length; i++) {
const lineIndex = generatedContentStartLine + i;
if (lineIndex < newLines.length) {
if (newLines[lineIndex] !== originalGeneratedLines[i]) {
generatedContentModified = true;
break;
}
} else {
// Generated content was deleted
generatedContentModified = true;
break;
}
}

if (generatedContentModified) {
// Generated content was modified - block the change
tick().then(() => {
// Force revert by not updating temporaryDisplayContent
temporaryDisplayContent = temporaryDisplayContent;
recalculateGeneratedContentLines();
highlightGeneratedContent();
});
return;
}

// Generated content is intact - allow the change
temporaryDisplayContent = newContent;

// Extract only the non-generated content for saving
const nonGeneratedLines = [];
const allLines = newContent.split("\n");

for (let i = 0; i < allLines.length; i++) {
// Only include lines that are NOT in the generated range
if (i < generatedContentStartLine || i > generatedContentEndLine) {
nonGeneratedLines.push(allLines[i]);
}
}

// Re-apply highlights immediately after any content change if we have generated content
const nonGeneratedContent = nonGeneratedLines.join("\n");
onTestsChange({ ...tests, script: nonGeneratedContent });
} else {
// Normal behavior when no generated content is protected
onTestsChange({ ...tests, script: newContent });
}

// Re-apply highlights if we have generated content
if (showGeneratedTestActions) {
if (rafId) cancelAnimationFrame(rafId);
rafId = requestAnimationFrame(() => {
highlightGeneratedContent();
// Double-check after a tiny delay
setTimeout(() => highlightGeneratedContent(), 1);
});
}
};

// Helper function to recalculate line positions after content changes
const recalculateGeneratedContentLines = () => {
if (!generatedTestContent || !temporaryDisplayContent) return;

const allLines = temporaryDisplayContent.split("\n");
const generatedLines = generatedTestContent.split("\n");

// Only look for generated content in a very narrow range around the expected position
// This prevents matching similar content elsewhere
let foundStart = -1;

// Search in a very small window around the current position
const searchStart = Math.max(0, generatedContentStartLine - 1);
const searchEnd = Math.min(
allLines.length - generatedLines.length + 1,
generatedContentStartLine + 3, // Very narrow search window
);

for (let i = searchStart; i < searchEnd; i++) {
let matches = true;

// Check if ALL lines match exactly
for (let j = 0; j < generatedLines.length; j++) {
if (i + j >= allLines.length || allLines[i + j] !== generatedLines[j]) {
matches = false;
break;
}
}

// Additional check: make sure we're not matching content that was there before
if (matches) {
// Verify this is actually the generated content block by checking context
// The generated content should be preceded by original content + separator
const hasProperContext =
i === 0 || // At the beginning
(originalTestContent && i > 0) || // After original content
(!originalTestContent && i === 0); // No original content, starts at beginning

if (hasProperContext) {
foundStart = i;
break;
}
}
}

if (foundStart >= 0) {
generatedContentStartLine = foundStart;
generatedContentEndLine = foundStart + generatedLines.length - 1;
}
};

const toggleLeftPanel = (): void => {
isLeftPanelCollapsed = !isLeftPanelCollapsed;
};

// Update the selectSnippet function to properly handle snippets without affecting generated content tracking
const selectSnippet = (data: string): void => {
let value = tests?.script || "";
value += value ? `\n${data}` : data;
onTestsChange({ ...tests, script: value });
let value = showGeneratedTestActions
? temporaryDisplayContent
: tests?.script || "";

const newValue = value ? `${value}\n${data}` : data;

if (showGeneratedTestActions) {
const snippetLines = data.split("\n").length;
const addedNewlines = value ? 1 : 0;

// Update the content first
temporaryDisplayContent = newValue;

// Calculate where the snippet was inserted
const oldLines = value.split("\n");
const insertionPoint = oldLines.length;

// Only adjust if snippet was inserted before generated content
if (insertionPoint <= generatedContentStartLine) {
generatedContentStartLine += snippetLines + addedNewlines;
generatedContentEndLine += snippetLines + addedNewlines;
}

// Track the snippet added during generation
contentAddedDuringGeneration = contentAddedDuringGeneration
? `${contentAddedDuringGeneration}\n${data}`
: data;

// DON'T update the stored tests.script during generation
// This preserves the original content for rejection

// Reapply highlights
setTimeout(() => {
highlightGeneratedContent();
}, 0);
} else {
// Normal behavior when no generated content is active
onTestsChange({ ...tests, script: newValue });
}
};

const highlightMatch = (text: string, searchTerm: string): string => {
Expand All @@ -137,6 +279,7 @@
const handleGenerateTestCases = async () => {
// Store the original content and current prompt
originalTestContent = tests?.script || "";
contentAddedDuringGeneration = "";
originalLineCount = originalTestContent.trim()
? originalTestContent.split("\n").length
: 0;
Expand All @@ -156,8 +299,8 @@
testCasePrompt = "";
generatedTestContent = result.generatedContent;

// Directly insert the generated content into the editor
await insertGeneratedContentDirectly();
// Show generated content temporarily (don't save to tests object)
await showGeneratedContentTemporarily();

// Show the action buttons
showGeneratedTestActions = true;
Expand All @@ -167,28 +310,32 @@
testCasePrompt = "";
}
};
const insertGeneratedContentDirectly = async () => {
// Insert the generated test content into the current script
const currentScript = tests?.script || "";

// Calculate the exact line positions where generated content will be placed
const currentLines = currentScript ? currentScript.split("\n") : [];
const showGeneratedContentTemporarily = async () => {
// Calculate line positions for highlighting
const currentScript = originalTestContent;
const separator = currentScript ? "\n\n" : "";

// Store where generated content starts
generatedContentStartLine = currentLines.length + (currentScript ? 2 : 0);
// Create the temporary display content
temporaryDisplayContent = currentScript + separator + generatedTestContent;

// Store where generated content ends
const generatedContentLines = generatedTestContent.split("\n");
generatedContentEndLine =
generatedContentStartLine + generatedContentLines.length - 1;
// Calculate exact line positions based on the original content length
const originalLines = currentScript ? currentScript.split("\n") : [];
const separatorLines = separator ? separator.split("\n") : [];

const newScript = currentScript + separator + generatedTestContent;
// Calculate start position: original content + separator
generatedContentStartLine =
originalLines.length + separatorLines.length - 1;
if (currentScript && separator) {
generatedContentStartLine = originalLines.length + 1; // +1 for the empty line from separator
} else if (!currentScript) {
generatedContentStartLine = 0;
}

onTestsChange({
...tests,
script: newScript,
});
// Calculate end position
const generatedLines = generatedTestContent.split("\n");
generatedContentEndLine =
generatedContentStartLine + generatedLines.length - 1;

await tick();

Expand Down Expand Up @@ -320,6 +467,7 @@
}
});
};

const removeHighlight = () => {
if (observer) {
observer.disconnect();
Expand Down Expand Up @@ -369,26 +517,47 @@
const acceptGeneratedTest = () => {
removeHighlight();

// Keep the generated content that's already in the editor
// Save the complete content (original + snippets + generated)
onTestsChange({ ...tests, script: temporaryDisplayContent });

// Clear temporary state
showGeneratedTestActions = false;
generatedTestContent = "";
originalTestContent = "";
temporaryDisplayContent = "";
currentPrompt = "";
originalLineCount = 0;
generatedContentStartLine = 0;
generatedContentEndLine = 0;
contentAddedDuringGeneration = ""; // Clear snippets tracker
};

const rejectGeneratedTest = () => {
removeHighlight();

// Revert to original content
onTestsChange({ ...tests, script: originalTestContent });
// Calculate what content to revert to:
// Original content + any snippets added during generation
let revertedContent = originalTestContent || "";

if (contentAddedDuringGeneration) {
revertedContent = revertedContent
? `${revertedContent}\n${contentAddedDuringGeneration}`
: contentAddedDuringGeneration;
}

// Save the reverted content (original + snippets, but no AI generation)
onTestsChange({ ...tests, script: revertedContent });

// Clear all temporary state
showGeneratedTestActions = false;
generatedTestContent = "";
originalTestContent = "";
temporaryDisplayContent = "";
currentPrompt = "";
originalLineCount = 0;
generatedContentStartLine = 0;
generatedContentEndLine = 0;
contentAddedDuringGeneration = "";
};

const regenerateTest = async () => {
Expand All @@ -399,7 +568,7 @@
showGeneratedTestActions = false;

// Revert to original content first
onTestsChange({ ...tests, script: originalTestContent });
temporaryDisplayContent = originalTestContent;

// Use the same prompt to regenerate
const result = await onGenerateTestCases(currentPrompt);
Expand All @@ -412,8 +581,8 @@
errorMessage = "";
generatedTestContent = result.generatedContent;

// Insert the new generated content
await insertGeneratedContentDirectly();
// Show the new generated content temporarily
await showGeneratedContentTemporarily();

// Show the action buttons again
showGeneratedTestActions = true;
Expand Down Expand Up @@ -549,7 +718,9 @@
>
<Editor
bind:lang
value={tests?.script || ""}
value={showGeneratedTestActions
? temporaryDisplayContent
: tests?.script || ""}
on:change={handleCodeMirrorChange}
isEditable={true}
autofocus={true}
Expand Down
Loading