diff --git a/tests/3_signal_pipeline/test.js b/tests/3_signal_pipeline/test.js
new file mode 100644
index 0000000..cea0627
--- /dev/null
+++ b/tests/3_signal_pipeline/test.js
@@ -0,0 +1,86 @@
+export default {
+ functionName: 'analyzeSignal',
+ prompt: `// Write an async JavaScript function 'analyzeSignal' that builds a signal processing pipeline chaining four libraries.
+// - The function accepts a YAML string describing signal parameters and returns analysis results.
+// - You MUST use dynamic import() to load ALL FOUR of these libraries from CDNs:
+// 1. 'js-yaml' — to parse the YAML config string into a JS object.
+// 2. 'mathjs' — to generate a composite sine wave signal using math.sin, math.pi, etc.
+// 3. 'ndarray' and 'ndarray-fft' — to perform an in-place Fast Fourier Transform on the signal.
+// 4. 'DOMPurify' — to sanitize the final HTML output string.
+//
+// YAML format:
+// sampleRate: 64
+// duration: 1
+// components:
+// - frequency: 5
+// amplitude: 1.0
+// - frequency: 12
+// amplitude: 0.5
+//
+// Pipeline steps:
+// 1. Parse the YAML string with js-yaml to extract config.
+// 2. Generate a time-domain signal array of length (sampleRate * duration).
+// For each sample index i, compute t = i / sampleRate, then sum over all components:
+// signal[i] = sum( amplitude * math.sin(2 * math.pi * frequency * t) )
+// Use mathjs functions (math.sin, math.pi) for the computation.
+// 3. Create two ndarray instances of length N (same as signal length):
+// - 'real' initialized with the signal values
+// - 'imag' initialized with all zeros
+// Run ndarray-fft(1, real, imag) to compute the forward FFT in-place.
+// 4. Compute the magnitude spectrum: for each bin k from 0 to N/2 (inclusive),
+// magnitude[k] = math.sqrt(real.get(k)**2 + imag.get(k)**2) / (N/2)
+// (Normalize by dividing by N/2 so that a pure sine of amplitude A gives a peak of ~A.)
+// 5. Find the dominant frequencies: collect all bins where magnitude > 0.1,
+// as objects { frequencyHz: k * sampleRate / N, magnitude: magnitude[k] }.
+// Sort them by magnitude descending. Round frequencyHz to the nearest integer.
+// Round magnitude to 2 decimal places.
+// 6. Build an HTML table string:
+//
| Frequency (Hz) | Magnitude |
+// For each dominant frequency: | {frequencyHz} | {magnitude} |
+//
+// 7. Sanitize the HTML string using DOMPurify.sanitize().
+// 8. Return an object: { peaks: dominantFrequenciesArray, html: sanitizedHTMLString, signalLength: N }
+// where dominantFrequenciesArray is the sorted array from step 5.`,
+ runTest: async (analyzeSignal) => {
+ const assert = {
+ strictEqual: (a, e, m) => { if (a !== e) throw new Error(m || `FAIL: ${a} !== ${e}`) },
+ ok: (v, m) => { if (!v) throw new Error(m) },
+ };
+
+ const yaml = `sampleRate: 64
+duration: 1
+components:
+ - frequency: 5
+ amplitude: 1.0
+ - frequency: 12
+ amplitude: 0.5`;
+
+ const result = await analyzeSignal(yaml);
+
+ assert.ok(result && typeof result === 'object', 'Should return an object');
+ assert.strictEqual(result.signalLength, 64, `signalLength should be 64, got ${result.signalLength}`);
+
+ assert.ok(Array.isArray(result.peaks), 'peaks should be an array');
+ assert.ok(result.peaks.length >= 2, `Should detect at least 2 peaks, got ${result.peaks.length}`);
+
+ const freqs = result.peaks.map(p => p.frequencyHz);
+ assert.ok(freqs.includes(5), `Should detect 5 Hz peak, found: ${freqs}`);
+ assert.ok(freqs.includes(12), `Should detect 12 Hz peak, found: ${freqs}`);
+
+ const peak5 = result.peaks.find(p => p.frequencyHz === 5);
+ const peak12 = result.peaks.find(p => p.frequencyHz === 12);
+ assert.ok(peak5.magnitude > 0.8 && peak5.magnitude < 1.2, `5 Hz magnitude should be ~1.0, got ${peak5.magnitude}`);
+ assert.ok(peak12.magnitude > 0.3 && peak12.magnitude < 0.7, `12 Hz magnitude should be ~0.5, got ${peak12.magnitude}`);
+
+ assert.ok(result.peaks[0].magnitude >= result.peaks[1].magnitude, 'Peaks should be sorted by magnitude descending');
+
+ assert.ok(typeof result.html === 'string', 'html should be a string');
+ assert.ok(result.html.includes(''), 'HTML should contain a table');
+ assert.ok(result.html.includes('Frequency (Hz)'), 'HTML should contain header');
+ assert.ok(result.html.includes('Magnitude'), 'HTML should contain Magnitude header');
+ assert.ok(result.html.includes('| 5 | '), 'HTML table should contain 5 Hz row');
+ assert.ok(result.html.includes('12 | '), 'HTML table should contain 12 Hz row');
+
+ assert.ok(!result.html.includes('