UI enhancemnts
This commit is contained in:
@@ -238,8 +238,97 @@ test('StockScorer', async (t) => {
|
||||
} as any;
|
||||
|
||||
const result = StockScorer.score(metrics, DEFAULT_RULES);
|
||||
// Should handle gracefully (zero is falsy, treated as null)
|
||||
// Zero quick ratio is a real value and fails the liquidity gate;
|
||||
// zero P/E, PEG, P/B are impossible values and are treated as missing.
|
||||
assert.ok(result);
|
||||
assert.equal(result.label, '🔴 REJECT');
|
||||
assert.ok(result.scoreSummary.includes('Quick'));
|
||||
});
|
||||
|
||||
await t.test('treats zero revenue growth as a real (stagnant) value', () => {
|
||||
const metrics: StockMetrics = {
|
||||
peRatio: 12,
|
||||
pegRatio: 0.8,
|
||||
debtToEquity: 0.5,
|
||||
quickRatio: 1.2,
|
||||
returnOnEquity: 20,
|
||||
operatingMargin: 15,
|
||||
netProfitMargin: 10,
|
||||
revenueGrowth: 0, // stagnant — must be scored, not skipped
|
||||
fcfYield: 5,
|
||||
} as any;
|
||||
|
||||
const result = StockScorer.score(metrics, DEFAULT_RULES);
|
||||
assert.ok(result.audit?.passedGates);
|
||||
// 0% growth is below revMed (5) → scores -1, same as slightly negative growth
|
||||
assert.equal(result.audit?.breakdown?.revenue, -1);
|
||||
});
|
||||
|
||||
await t.test('treats zero debt-to-equity as debt-free, not missing', () => {
|
||||
const metrics: StockMetrics = {
|
||||
peRatio: 12,
|
||||
pegRatio: 0.8,
|
||||
debtToEquity: 0, // debt-free — should pass the gate, not be skipped
|
||||
quickRatio: 1.2,
|
||||
returnOnEquity: 20,
|
||||
operatingMargin: 15,
|
||||
netProfitMargin: 10,
|
||||
revenueGrowth: 8,
|
||||
fcfYield: 5,
|
||||
} as any;
|
||||
|
||||
const result = StockScorer.score(metrics, DEFAULT_RULES);
|
||||
assert.ok(result.audit?.passedGates);
|
||||
assert.notEqual(result.label, '🔴 REJECT');
|
||||
});
|
||||
|
||||
await t.test('flags insufficient data instead of plain HOLD', () => {
|
||||
const metrics: StockMetrics = { currentPrice: 50 } as any;
|
||||
|
||||
const result = StockScorer.score(metrics, DEFAULT_RULES);
|
||||
assert.equal(result.label, '🟡 HOLD (No Data)');
|
||||
assert.equal(result.audit?.coverage?.active, 0);
|
||||
});
|
||||
|
||||
await t.test('returns structured tier and numeric score (P0.3)', () => {
|
||||
const strong: StockMetrics = {
|
||||
peRatio: 12,
|
||||
pegRatio: 0.7,
|
||||
debtToEquity: 0.3,
|
||||
quickRatio: 1.5,
|
||||
returnOnEquity: 30,
|
||||
operatingMargin: 25,
|
||||
netProfitMargin: 18,
|
||||
revenueGrowth: 12,
|
||||
fcfYield: 6,
|
||||
} as any;
|
||||
const pass = StockScorer.score(strong, DEFAULT_RULES);
|
||||
assert.equal(pass.tier, 'PASS');
|
||||
assert.ok(typeof pass.score === 'number' && pass.score >= 4);
|
||||
|
||||
const gated: StockMetrics = { ...strong, peRatio: 40 } as any;
|
||||
const reject = StockScorer.score(gated, DEFAULT_RULES);
|
||||
assert.equal(reject.tier, 'REJECT');
|
||||
assert.equal(reject.score, null);
|
||||
|
||||
const noData = StockScorer.score({ currentPrice: 50 } as any, DEFAULT_RULES);
|
||||
assert.equal(noData.tier, 'HOLD');
|
||||
assert.equal(noData.score, 0);
|
||||
});
|
||||
|
||||
await t.test('reports factor coverage in audit', () => {
|
||||
const metrics: StockMetrics = {
|
||||
peRatio: 12,
|
||||
pegRatio: 0.8,
|
||||
quickRatio: 1.2,
|
||||
returnOnEquity: 20,
|
||||
currentPrice: 50,
|
||||
} as any;
|
||||
|
||||
const result = StockScorer.score(metrics, DEFAULT_RULES);
|
||||
assert.ok(result.audit?.coverage);
|
||||
assert.ok(result.audit.coverage.active >= 1);
|
||||
assert.ok(result.audit.coverage.active <= result.audit.coverage.total);
|
||||
});
|
||||
|
||||
await t.test('scores based on configured thresholds', () => {
|
||||
|
||||
Reference in New Issue
Block a user