Merge pull request #3 from nmemmert/claude/test-coverage-analysis-R2olv
Fix Greek lookup: auto-prepend G prefix and use search endpoint for E…
This commit is contained in:
+22
-8
@@ -866,6 +866,7 @@ const App = () => {
|
|||||||
return Array.isArray(definitions) && definitions.length > 0 ? definitions : null;
|
return Array.isArray(definitions) && definitions.length > 0 ? definitions : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Greek Strong's number or unknown — try BDAG (Greek NT lexicon) first.
|
||||||
const greekResponse = await fetch(greekUrl);
|
const greekResponse = await fetch(greekUrl);
|
||||||
if (greekResponse.ok) {
|
if (greekResponse.ok) {
|
||||||
const greekDefinitions = await greekResponse.json();
|
const greekDefinitions = await greekResponse.json();
|
||||||
@@ -874,10 +875,19 @@ const App = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const hebrewResponse = await fetch(hebrewUrl);
|
// English word — try full-text search filtered to Greek entries.
|
||||||
if (!hebrewResponse.ok) return null;
|
if (!isGreekStrongNumber(query)) {
|
||||||
const hebrewDefinitions = await hebrewResponse.json();
|
const searchResponse = await fetch(`https://bolls.life/search-dictionaries/BDAG/${encoded}/`);
|
||||||
return Array.isArray(hebrewDefinitions) && hebrewDefinitions.length > 0 ? hebrewDefinitions : null;
|
if (searchResponse.ok) {
|
||||||
|
const results = await searchResponse.json();
|
||||||
|
const greekResults = Array.isArray(results)
|
||||||
|
? results.filter((r) => String(r.topic ?? '').startsWith('G'))
|
||||||
|
: [];
|
||||||
|
if (greekResults.length > 0) return [greekResults[0]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const lookupGreekWord = async (chunkId, wordId) => {
|
const lookupGreekWord = async (chunkId, wordId) => {
|
||||||
@@ -886,8 +896,12 @@ const App = () => {
|
|||||||
if (!word || !word.query.trim()) return;
|
if (!word || !word.query.trim()) return;
|
||||||
updateChunkWord(chunkId, wordId, { loading: true });
|
updateChunkWord(chunkId, wordId, { loading: true });
|
||||||
try {
|
try {
|
||||||
const definitions = await fetchBollsDefinition(word.query);
|
// Normalize bare numbers to G prefix: "4102" → "G4102" (Greek, not Hebrew H4102).
|
||||||
if (!definitions) {
|
const raw = word.query.trim();
|
||||||
|
const normalized = /^\d+$/.test(raw) ? `G${raw}` : /^[gGhH]\d+$/.test(raw) ? raw.toUpperCase() : raw;
|
||||||
|
|
||||||
|
const definitions = await fetchBollsDefinition(normalized);
|
||||||
|
if (!definitions || definitions.length === 0) {
|
||||||
updateChunkWord(chunkId, wordId, {
|
updateChunkWord(chunkId, wordId, {
|
||||||
strongNumber: '',
|
strongNumber: '',
|
||||||
shortDefinition: 'No definition found.',
|
shortDefinition: 'No definition found.',
|
||||||
@@ -898,7 +912,7 @@ const App = () => {
|
|||||||
const first = definitions[0];
|
const first = definitions[0];
|
||||||
const extractedPartOfSpeech = extractPartOfSpeech(first.definition || '');
|
const extractedPartOfSpeech = extractPartOfSpeech(first.definition || '');
|
||||||
updateChunkWord(chunkId, wordId, {
|
updateChunkWord(chunkId, wordId, {
|
||||||
strongNumber: first.topic || word.query,
|
strongNumber: first.topic || normalized,
|
||||||
lexeme: first.lexeme || '',
|
lexeme: first.lexeme || '',
|
||||||
transliteration: first.transliteration || '',
|
transliteration: first.transliteration || '',
|
||||||
partOfSpeech: extractedPartOfSpeech || word.partOfSpeech || '',
|
partOfSpeech: extractedPartOfSpeech || word.partOfSpeech || '',
|
||||||
@@ -1633,7 +1647,7 @@ const App = () => {
|
|||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
if (e.key === 'Enter') { e.preventDefault(); lookupGreekWord(selectedChunk.id, word.id); }
|
if (e.key === 'Enter') { e.preventDefault(); lookupGreekWord(selectedChunk.id, word.id); }
|
||||||
}}
|
}}
|
||||||
placeholder="G4102 or faith"
|
placeholder="G4102, 4102, or faith (Greek)"
|
||||||
className="mt-2 block w-full rounded-2xl border border-slate-300 bg-slate-50 px-3 py-2 text-slate-900 shadow-sm focus:border-sky-500 focus:outline-none focus:ring-2 focus:ring-sky-200"
|
className="mt-2 block w-full rounded-2xl border border-slate-300 bg-slate-50 px-3 py-2 text-slate-900 shadow-sm focus:border-sky-500 focus:outline-none focus:ring-2 focus:ring-sky-200"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
+7
-7
@@ -289,18 +289,18 @@ async function goToStudyAndAddGreekWord(fetchMock) {
|
|||||||
fireEvent.click(screen.getByRole('button', { name: /begin studying/i }));
|
fireEvent.click(screen.getByRole('button', { name: /begin studying/i }));
|
||||||
await screen.findByText(/chunk editor/i);
|
await screen.findByText(/chunk editor/i);
|
||||||
fireEvent.click(screen.getByRole('button', { name: /add greek word/i }));
|
fireEvent.click(screen.getByRole('button', { name: /add greek word/i }));
|
||||||
await screen.findByPlaceholderText(/G4102 or faith/i);
|
await screen.findByPlaceholderText(/G4102, 4102/i);
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Greek word lookup', () => {
|
describe('Greek word lookup', () => {
|
||||||
test('adds a Greek word entry form', async () => {
|
test('adds a Greek word entry form', async () => {
|
||||||
await goToStudyAndAddGreekWord();
|
await goToStudyAndAddGreekWord();
|
||||||
expect(screen.getByPlaceholderText(/G4102 or faith/i)).toBeInTheDocument();
|
expect(screen.getByPlaceholderText(/G4102, 4102/i)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('populates fields after a successful lookup', async () => {
|
test('populates fields after a successful lookup', async () => {
|
||||||
await goToStudyAndAddGreekWord(buildFetchMock({ greekData: mockGreekDefinition }));
|
await goToStudyAndAddGreekWord(buildFetchMock({ greekData: mockGreekDefinition }));
|
||||||
fireEvent.change(screen.getByPlaceholderText(/G4102 or faith/i), { target: { value: 'G4102' } });
|
fireEvent.change(screen.getByPlaceholderText(/G4102, 4102/i), { target: { value: 'G4102' } });
|
||||||
fireEvent.click(screen.getByRole('button', { name: /look up/i }));
|
fireEvent.click(screen.getByRole('button', { name: /look up/i }));
|
||||||
await screen.findByDisplayValue('πίστις');
|
await screen.findByDisplayValue('πίστις');
|
||||||
expect(screen.getByDisplayValue('pistis')).toBeInTheDocument();
|
expect(screen.getByDisplayValue('pistis')).toBeInTheDocument();
|
||||||
@@ -309,7 +309,7 @@ describe('Greek word lookup', () => {
|
|||||||
|
|
||||||
test('shows "No definition found." when the API returns an empty array', async () => {
|
test('shows "No definition found." when the API returns an empty array', async () => {
|
||||||
await goToStudyAndAddGreekWord(buildFetchMock({ greekData: [] }));
|
await goToStudyAndAddGreekWord(buildFetchMock({ greekData: [] }));
|
||||||
fireEvent.change(screen.getByPlaceholderText(/G4102 or faith/i), { target: { value: 'G4102' } });
|
fireEvent.change(screen.getByPlaceholderText(/G4102, 4102/i), { target: { value: 'G4102' } });
|
||||||
fireEvent.click(screen.getByRole('button', { name: /look up/i }));
|
fireEvent.click(screen.getByRole('button', { name: /look up/i }));
|
||||||
await screen.findByDisplayValue('No definition found.');
|
await screen.findByDisplayValue('No definition found.');
|
||||||
});
|
});
|
||||||
@@ -326,7 +326,7 @@ describe('Greek word lookup', () => {
|
|||||||
return Promise.reject(new Error('Network error'));
|
return Promise.reject(new Error('Network error'));
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
fireEvent.change(screen.getByPlaceholderText(/G4102 or faith/i), { target: { value: 'G4102' } });
|
fireEvent.change(screen.getByPlaceholderText(/G4102, 4102/i), { target: { value: 'G4102' } });
|
||||||
fireEvent.click(screen.getByRole('button', { name: /look up/i }));
|
fireEvent.click(screen.getByRole('button', { name: /look up/i }));
|
||||||
await screen.findByDisplayValue('Lookup failed.');
|
await screen.findByDisplayValue('Lookup failed.');
|
||||||
});
|
});
|
||||||
@@ -343,10 +343,10 @@ describe('Greek word lookup', () => {
|
|||||||
|
|
||||||
test('removes a Greek word entry', async () => {
|
test('removes a Greek word entry', async () => {
|
||||||
await goToStudyAndAddGreekWord();
|
await goToStudyAndAddGreekWord();
|
||||||
expect(screen.getByPlaceholderText(/G4102 or faith/i)).toBeInTheDocument();
|
expect(screen.getByPlaceholderText(/G4102, 4102/i)).toBeInTheDocument();
|
||||||
fireEvent.click(screen.getByRole('button', { name: /^delete$/i }));
|
fireEvent.click(screen.getByRole('button', { name: /^delete$/i }));
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(screen.queryByPlaceholderText(/G4102 or faith/i)).not.toBeInTheDocument();
|
expect(screen.queryByPlaceholderText(/G4102, 4102/i)).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user