Skip to content
Merged
Show file tree
Hide file tree
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
65 changes: 65 additions & 0 deletions frontend/src/__tests__/settings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,71 @@ describe('Settings Module', () => {
expect(coverageSentFor('savings-plans-compute')).toBe(20);
});

test('a rejected global PUT leaves the coverage dirty and sends no service PUTs', async () => {
await loadWithCoverage50();
(document.getElementById('setting-default-coverage') as HTMLInputElement).value = '70';
(api.updateConfig as jest.Mock).mockRejectedValueOnce(new Error('boom'));

await saveGlobalSettings({ preventDefault: jest.fn() } as unknown as Event);

expect(api.updateServiceConfig).not.toHaveBeenCalled();
expect(isUnsavedChanges()).toBe(true);
});

test('an SP card coverage whose service PUT failed stays dirty and is resent', async () => {
await loadWithCoverage50();
(document.getElementById('aws-savings-plans-compute-coverage') as HTMLInputElement).value = '70';
(api.updateServiceConfig as jest.Mock).mockImplementation(async (provider: string, svc: string) => {
if (provider === 'aws' && svc === 'savings-plans-compute') throw new Error('sp failed');
});

await saveGlobalSettings({ preventDefault: jest.fn() } as unknown as Event);
expect(isUnsavedChanges()).toBe(true);

(api.updateServiceConfig as jest.Mock).mockClear().mockResolvedValue(undefined);
await saveGlobalSettings({ preventDefault: jest.fn() } as unknown as Event);

expect(coverageSentFor('savings-plans-compute')).toBe(70);
expect(isUnsavedChanges()).toBe(false);
});

test('an SP card coverage typed during its pending PUT is kept and sent by the next save (#576)', async () => {
await loadWithCoverage50();
const card = document.getElementById('aws-savings-plans-compute-coverage') as HTMLInputElement;
card.value = '60';
let resolvePut!: () => void;
(api.updateServiceConfig as jest.Mock).mockImplementation((provider: string, svc: string) =>
provider === 'aws' && svc === 'savings-plans-compute'
? new Promise<void>(resolve => { resolvePut = resolve; })
: Promise.resolve());

const firstSave = saveGlobalSettings({ preventDefault: jest.fn() } as unknown as Event);
await new Promise(r => setTimeout(r, 0));
card.value = '77';
resolvePut();
await firstSave;

expect(coverageSentFor('savings-plans-compute')).toBe(60);
expect(card.value).toBe('77');
expect(isUnsavedChanges()).toBe(true);

(api.updateServiceConfig as jest.Mock).mockClear().mockResolvedValue(undefined);
await saveGlobalSettings({ preventDefault: jest.fn() } as unknown as Event);

expect(coverageSentFor('savings-plans-compute')).toBe(77);
expect(isUnsavedChanges()).toBe(false);
});

test('a saved service payment edit leaves nothing unsaved (#576)', async () => {
await loadWithCoverage50();
(document.getElementById('aws-ec2-payment') as HTMLSelectElement).value = 'partial-upfront';
expect(isUnsavedChanges()).toBe(true);

await saveGlobalSettings({ preventDefault: jest.fn() } as unknown as Event);

expect(isUnsavedChanges()).toBe(false);
});

test('an empty global coverage field is rejected instead of saving 0', async () => {
await loadWithCoverage50();
(document.getElementById('setting-default-coverage') as HTMLInputElement).value = '';
Expand Down
11 changes: 6 additions & 5 deletions frontend/src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3591,9 +3591,7 @@ export async function saveGlobalSettings(e: Event): Promise<void> {
// controls. Read from the DOM when the card has the controls; fall back
// to the base row value (or the global default) otherwise so RI and
// Azure/GCP cards continue to inherit the global settings.
let coverage = defaultCoverageChanged
? settings.default_coverage
: (base?.coverage ?? settings.default_coverage);
let coverage = base?.coverage ?? settings.default_coverage;
let enabled = base?.enabled ?? true;
if ('coverageId' in field && field.coverageId) {
const rawCov = byId<HTMLInputElement>(field.coverageId)?.value ?? '';
Expand Down Expand Up @@ -3636,8 +3634,11 @@ export async function saveGlobalSettings(e: Event): Promise<void> {
];
if ('coverageId' in field && field.coverageId) {
const el = byId<HTMLInputElement>(field.coverageId);
if (el) el.value = String(coverage);
savedSnapshot[field.coverageId] = getFieldValue(field.coverageId);
// Show the persisted (possibly propagated) value only when the card
// still holds what this Save read; an edit typed during the PUT stays
// dirty against the persisted value and goes out with the next Save.
if (el && getFieldValue(field.coverageId) === sentFields[field.coverageId]) el.value = String(coverage);
savedSnapshot[field.coverageId] = String(coverage);
}
});
});
Expand Down
Loading