sflow
Version:
sflow is a powerful and highly-extensible library designed for processing and manipulating streams of data effortlessly. Inspired by the functional programming paradigm, it provides a rich set of utilities for transforming streams, including chunking, fil
346 lines (281 loc) • 12.9 kB
text/typescript
import { sf } from ".";
import { sflow } from "./sflow";
import { matchAlls, matchs, replaceAlls, replaces } from "./strings";
describe("strings functions", () => {
describe("matchs", () => {
it("should match strings using regexp", async () => {
const regexp = /(\d+)/;
const transformStream = matchs(regexp);
const writer = transformStream.writable.getWriter();
const reader = transformStream.readable.getReader();
writer.write("hello 123 world");
writer.write("no numbers here");
writer.write("456 numbers");
writer.close();
const results = [];
while (true) {
const { value, done } = await reader.read();
if (done) break;
results.push(value);
}
expect(results).toHaveLength(3);
expect(results[0]?.[0]).toBe("123");
expect(results[0]?.[1]).toBe("123");
expect(results[1]).toBe(null);
expect(results[2]?.[0]).toBe("456");
});
it("should work with custom matcher objects", async () => {
const customMatcher = {
[Symbol.match]: (str: string) => {
const match = str.match(/test/);
return match;
}
};
const transformStream = matchs(customMatcher);
const writer = transformStream.writable.getWriter();
const reader = transformStream.readable.getReader();
writer.write("this is a test");
writer.write("no match here");
writer.close();
const results = [];
while (true) {
const { value, done } = await reader.read();
if (done) break;
results.push(value);
}
expect(results).toHaveLength(2);
expect(results[0]?.[0]).toBe("test");
expect(results[1]).toBe(null);
});
it("should integrate with sflow", async () => {
const result = await sflow(["hello 123", "world 456", "no match"])
.through(matchs(/(\d+)/))
.map(match => match?.[1] || "none")
.toArray();
expect(result).toEqual(["123", "456", "none"]);
});
});
describe("matchAlls", () => {
it("should find all matches in strings", async () => {
const regexp = /\d+/g;
const transformStream = matchAlls(regexp);
const writer = transformStream.writable.getWriter();
const reader = transformStream.readable.getReader();
writer.write("123 and 456 and 789");
writer.write("no numbers");
writer.write("999");
writer.close();
const results = [];
while (true) {
const { value, done } = await reader.read();
if (done) break;
results.push([...value]); // Convert iterator to array
}
expect(results).toHaveLength(3);
expect(results[0]).toHaveLength(3);
expect(results[0][0][0]).toBe("123");
expect(results[0][1][0]).toBe("456");
expect(results[0][2][0]).toBe("789");
expect(results[1]).toHaveLength(0);
expect(results[2]).toHaveLength(1);
expect(results[2][0][0]).toBe("999");
});
it("should integrate with sflow", async () => {
const result = await sflow(["abc 1 def 2", "ghi 3"])
.through(matchAlls(/\d/g))
.map(matches => [...matches].map(m => m[0]))
.toArray();
expect(result).toEqual([["1", "2"], ["3"]]);
});
});
describe("replaces", () => {
it("should replace using string searchValue and string replacement", async () => {
const transformStream = sf.replaces("hello", "hi");
const writer = transformStream.writable.getWriter();
const reader = transformStream.readable.getReader();
writer.write("hello world");
writer.write("say hello");
writer.write("hello hello");
writer.close();
const results = [];
while (true) {
const { value, done } = await reader.read();
if (done) break;
results.push(value);
}
expect(results).toEqual(["hi world", "say hi", "hi hello"]);
});
it("should replace using regexp and string replacement", async () => {
const transformStream = replaces(/\d+/, "NUMBER");
const writer = transformStream.writable.getWriter();
const reader = transformStream.readable.getReader();
writer.write("I have 123 apples");
writer.write("No numbers here");
writer.write("456 and 789");
writer.close();
const results = [];
while (true) {
const { value, done } = await reader.read();
if (done) break;
results.push(value);
}
expect(results).toEqual(["I have NUMBER apples", "No numbers here", "NUMBER and 789"]);
});
it("should replace using regexp and function replacement", async () => {
const transformStream = replaces(/(\d+)/, (match, p1) => `[${p1}]`);
const writer = transformStream.writable.getWriter();
const reader = transformStream.readable.getReader();
writer.write("I have 123 apples");
writer.write("No numbers here");
writer.close();
const results = [];
while (true) {
const { value, done } = await reader.read();
if (done) break;
results.push(value);
}
expect(results).toEqual(["I have [123] apples", "No numbers here"]);
});
it("should replace using async function replacement", async () => {
const transformStream = replaces(/(\d+)/, async (match, p1) => {
await new Promise(resolve => setTimeout(resolve, 1));
return `[${p1}]`;
});
const writer = transformStream.writable.getWriter();
const reader = transformStream.readable.getReader();
writer.write("I have 123 apples");
writer.close();
const results = [];
while (true) {
const { value, done } = await reader.read();
if (done) break;
results.push(value);
}
expect(results).toEqual(["I have [123] apples"]);
});
it("should work with custom replace objects", async () => {
const customReplacer = {
[Symbol.replace]: (str: string, replaceValue: string) => {
return str.replace(/test/g, replaceValue);
}
};
const transformStream = sf.replaces(customReplacer, "REPLACED");
const writer = transformStream.writable.getWriter();
const reader = transformStream.readable.getReader();
writer.write("this is a test string");
writer.close();
const results = [];
while (true) {
const { value, done } = await reader.read();
if (done) break;
results.push(value);
}
expect(results).toEqual(["this is a REPLACED string"]);
});
it("should integrate with sflow", async () => {
const result = await sflow(["hello world", "hello universe"])
.through(replaces("hello", "hi"))
.toArray();
expect(result).toEqual(["hi world", "hi universe"]);
});
});
describe("replaceAlls", () => {
it("should replace all occurrences using string searchValue and string replacement", async () => {
const transformStream = replaceAlls("hello", "hi");
const writer = transformStream.writable.getWriter();
const reader = transformStream.readable.getReader();
writer.write("hello world hello");
writer.write("say hello hello hello");
writer.write("goodbye");
writer.close();
const results = [];
while (true) {
const { value, done } = await reader.read();
if (done) break;
results.push(value);
}
expect(results).toEqual(["hi world hi", "say hi hi hi", "goodbye"]);
});
it("should replace all occurrences using regexp and string replacement", async () => {
const transformStream = replaceAlls(/\d+/g, "NUMBER");
const writer = transformStream.writable.getWriter();
const reader = transformStream.readable.getReader();
writer.write("I have 123 apples and 456 oranges");
writer.write("No numbers here");
writer.write("789");
writer.close();
const results = [];
while (true) {
const { value, done } = await reader.read();
if (done) break;
results.push(value);
}
expect(results).toEqual(["I have NUMBER apples and NUMBER oranges", "No numbers here", "NUMBER"]);
});
it("should replace all occurrences using function replacement", async () => {
const transformStream = replaceAlls(/(\d+)/g, (match, p1) => `[${p1}]`);
const writer = transformStream.writable.getWriter();
const reader = transformStream.readable.getReader();
writer.write("I have 123 apples and 456 oranges");
writer.write("No numbers here");
writer.close();
const results = [];
while (true) {
const { value, done } = await reader.read();
if (done) break;
results.push(value);
}
expect(results).toEqual(["I have [123] apples and [456] oranges", "No numbers here"]);
});
it("should replace all occurrences using async function replacement", async () => {
const transformStream = replaceAlls(/(\d+)/g, async (match, p1) => {
await new Promise(resolve => setTimeout(resolve, 1));
return `[${p1}]`;
});
const writer = transformStream.writable.getWriter();
const reader = transformStream.readable.getReader();
writer.write("I have 123 apples and 456 oranges");
writer.close();
const results = [];
while (true) {
const { value, done } = await reader.read();
if (done) break;
results.push(value);
}
expect(results).toEqual(["I have [123] apples and [456] oranges"]);
});
it("should integrate with sflow", async () => {
const result = await sflow(["hello world hello", "hello universe hello"])
.through(replaceAlls("hello", "hi"))
.toArray();
expect(result).toEqual(["hi world hi", "hi universe hi"]);
});
});
describe("edge cases", () => {
it("should handle empty strings", async () => {
const results = await Promise.all([
sflow(["", "test", ""]).through(matchs(/test/)).toArray(),
sflow(["", "test", ""]).through(matchAlls(/test/g)).map(matches => [...matches]).toArray(),
sflow(["", "test", ""]).through(replaces("test", "replaced")).toArray(),
sflow(["", "test", ""]).through(replaceAlls("test", "replaced")).toArray(),
]);
expect(results[0]).toEqual([null, expect.any(Array), null]);
expect(results[1]).toEqual([[], expect.any(Array), []]);
expect(results[2]).toEqual(["", "replaced", ""]);
expect(results[3]).toEqual(["", "replaced", ""]);
});
it("should handle special regex characters in string replacement", async () => {
const result = await sflow(["test $1 test"])
.through(replaceAlls("test", "[$1]"))
.toArray();
expect(result).toEqual(["[$1] $1 [$1]"]);
});
it("should handle multiple transform streams in sequence", async () => {
const result = await sflow(["hello 123 world 456"])
.through(replaces(/(\d+)/, "[$1]"))
.through(replaceAlls("hello", "hi"))
.toArray();
expect(result).toEqual(["hi [123] world 456"]);
});
});
});