Skip to content

Commit

Permalink
feat(run): Allow zip path as first argument (#3)
Browse files Browse the repository at this point in the history
...to allow usage with other zip files.
- exposed formerly internal methods `contentLoader` and `entriesLoader`
  - both of them now use a (zip file) location based in memory `cache`
- `xmltest.json` is now only required as part of `getEntries`, not when the module is loaded. It's also the only method that uses those entries.
- tests for `run` method
  • Loading branch information
karfau committed Jan 23, 2021
1 parent f030605 commit bdaedb5
Show file tree
Hide file tree
Showing 9 changed files with 232 additions and 49 deletions.
7 changes: 6 additions & 1 deletion .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,9 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test
- name: check if json up to date
run: |
npm run xmltest.json
git add . # make sure line endings are sanitized by git
git diff HEAD --exit-code
- run: npm run test.zip && npm test
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,23 @@ All methods have doc comments that include types.
- `getContent`
- `getEntries`
- `load`
- `contentLoader`
- `entriesLoader`
- `replaceWithWrappedCodePointAt`
- `replaceNonTextChars`
- `run`

(Feel free to contribute by automating the extraction of the documentation to this or another file.)

### with different zip files

The API can be used with other zip files by passing relative or absolute file names as arguments:
- `load` (second argument)
- `run` (first argument)

## Related Resources

- The page of the author linking to xmltest.zip: <http://www.jclark.com/xml/>
- THe way I found those testcases since they are part of a bigger testsuite for (Java SAX parsers) <http://cafeconleche.org/SAXTest/>
- The way I found those testcases since they are part of a bigger testsuite for (Java SAX parsers) <http://cafeconleche.org/SAXTest/>
- The W3C also provides an XML test suite: <https://www.w3.org/XML/Test/> (the files in `xmltest.zip` are part of this but there is no clear license for the whole package)
- The PR that initially led to the creation of this package: <https://github.com/xmldom/xmldom/pull/112>
16 changes: 16 additions & 0 deletions cache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
exports.cache = () => {
let map = new Map();

return {
clear: () => {
map = new Map();
},
delete: (key) => map.delete(key),
get: (key) => map.get(key),
has: (key) => map.has(key),
keys: () => [...map.keys()],
set: (key, value) => {
map.set(key, value);
},
};
};
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"node": ">=10"
},
"files": [
"cache.js",
"README.md",
"LICENSE",
"xmltest.zip",
Expand All @@ -18,7 +19,9 @@
"scripts": {
"extract": "npx extract-zip xmltest.zip $PWD/data",
"test": "jest",
"start": "jest --watch"
"test.zip": "./test.zip.sh",
"start": "npm run test -- --watch",
"xmltest.json": "runex ./xmltest.js > xmltest.json"
},
"repository": {
"type": "git",
Expand Down
Binary file added test.zip
Binary file not shown.
14 changes: 14 additions & 0 deletions test.zip.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/sh
# Creates test.zip file in the same directory

# clear the folder if it already existed
rm -rf data/folder
# the `data` directory is .gitignored, so we can use it as temp
mkdir -p data/folder
# add one file that has content
echo CONTENT > data/folder/file.ext
# and one empty file
touch data/folder/empty

# It is important for the tests to have the optional `folder` entry in the zip file.
(cd data && zip ../test.zip folder folder/*)
46 changes: 46 additions & 0 deletions test/cache.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const { cache } = require("../cache");

describe("cache", () => {
test("should have no keys initially", () => {
expect(cache().keys()).toHaveLength(0);
});
test.each(["key", 0, 1, "", null, NaN, undefined])(
"should store value for key `%s`",
(key) => {
const value = {};
const it = cache();

expect(it.has(key)).toBe(false);
it.set(key, value);
expect(it.has(key)).toBe(true);
expect(it.keys()).toHaveLength(1);
expect(it.get(key)).toBe(value);
}
);
test.each(["key", 0, 1, "", null, NaN, undefined])(
"should return undefined for key `%s`",
(key) => {
const it = cache();

expect(it.has(key)).toBe(false);
expect(it.get(key)).toBeUndefined();
}
);
test.each(["key", 0, 1, "", null, NaN, undefined])(
"should delete key for key `%s`",
(key) => {
const it = cache();
it.set(key, {});
it.delete(key);
expect(it.has(key)).toBe(false);
expect(it.get(key)).toBeUndefined();
}
);
test("should clear the cache", () => {
const it = cache();
it.set("key", {});
it.clear();
expect(it.has("key")).toBe(false);
expect(it.keys()).toHaveLength(0);
});
});
61 changes: 61 additions & 0 deletions test/run.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
const entries = require("../xmltest.json");
const { run, contentLoader, entriesLoader } = require("../xmltest.js");
const path = require("path");

const README_PATH = "xmltest/readme.html";
const TEST_ZIP_PATH = path.join(__dirname, "..", "test.zip");

const TEST_ZIP_ENTRIES = {
"folder/": "",
"folder/file.ext": "file.ext",
"folder/empty": "empty",
};

describe("run", () => {
beforeEach(contentLoader.CACHE.clear);
beforeEach(entriesLoader.CACHE.clear);
describe("only filter arguments", () => {
test("should return entries without any arguments", async () => {
// FYI: xmltest.zip doesn't contain any folder entries
expect(await run()).toEqual(entries);
expect(contentLoader.CACHE.keys()).toHaveLength(0);
expect(entriesLoader.CACHE.keys()).toHaveLength(1);
});
test("should return all (file) keys in entries with first argument 'xmltest'", async () => {
expect(Object.keys(await run("xmltest"))).toEqual(Object.keys(entries));
expect(contentLoader.CACHE.keys()).toHaveLength(1);
expect(entriesLoader.CACHE.keys()).toHaveLength(0);
});
test("should return the content of readme.html with first argument 'xmltest/readme.html'", async () => {
expect(await run(README_PATH)).toMatch(/^<HTML>.*/);
});
test("should return dict with single key when multiple filters only match one entry", async () => {
const actual = await run(...README_PATH.split("/"));
expect(Object.keys(actual)).toHaveLength(1);
expect(actual[README_PATH]).toMatch(/^<HTML>.*/);
});
});
describe("first argument is path to zip", () => {
test.each(["./test.zip", "../xmltest/test.zip", TEST_ZIP_PATH])(
"should return all entries without any filter arguments %s",
async (pathToZip) => {
expect(await run(pathToZip)).toEqual(TEST_ZIP_ENTRIES);
}
);
test("should return all file keys in entries with first filter argument 'folder'", async () => {
const actual = await run(TEST_ZIP_PATH, "folder");
expect(Object.keys(actual)).toEqual(
Object.keys(TEST_ZIP_ENTRIES).filter((entry) => !entry.endsWith("/"))
);
});
test("should return the content when first filter argument matches a file", async () => {
expect(await run(TEST_ZIP_PATH, "folder/file.ext")).toBe("CONTENT\n");
expect(await run(TEST_ZIP_PATH, "folder/empty")).toBe("");
});
test("should return dict with single key when multiple filters only match one entry", async () => {
const actual = await run(TEST_ZIP_PATH, "folder", "file");
expect(Object.keys(actual)).toHaveLength(1);
expect(actual["folder/file.ext"]).toMatch("CONTENT\n");
});
});
});

0 comments on commit bdaedb5

Please sign in to comment.