Skip to content

Commit 660b559

Browse files
authored
Merge pull request #251 from ff6347/feat/work-without-xsel
2 parents 61ba380 + 816da3c commit 660b559

File tree

15 files changed

+439
-138
lines changed

15 files changed

+439
-138
lines changed

.devcontainer/devcontainer.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
2+
// README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node
3+
{
4+
"name": "Node.js & TypeScript",
5+
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
6+
"image": "mcr.microsoft.com/devcontainers/typescript-node:0-18"
7+
8+
// Features to add to the dev container. More info: https://containers.dev/features.
9+
// "features": {},
10+
11+
// Use 'forwardPorts' to make a list of ports inside the container available locally.
12+
// "forwardPorts": [],
13+
14+
// Use 'postCreateCommand' to run commands after the container is created.
15+
// "postCreateCommand": "yarn install",
16+
17+
// Configure tool-specific properties.
18+
// "customizations": {},
19+
20+
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
21+
// "remoteUser": "root"
22+
}

.github/workflows/test.yml

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,44 @@ on:
77
branches: [master]
88

99
jobs:
10-
build:
10+
test_no_xsel:
1111
runs-on: ubuntu-latest
1212

1313
strategy:
1414
matrix:
15-
node-version: [16.x, 18.x]
15+
node-version: [16.x, 18.x, 20.x]
16+
17+
steps:
18+
- uses: actions/checkout@v3
19+
- name: Use Node.js ${{ matrix.node-version }}
20+
uses: actions/setup-node@v3
21+
with:
22+
node-version: ${{ matrix.node-version }}
23+
- name: npm install, build, and test
24+
run: |
25+
npm ci
26+
npm run build --if-present
27+
npm test
28+
env:
29+
CI: true
30+
NODE_ENV: test
31+
# - name: shell test
32+
# run: |
33+
# npm link
34+
# ./__tests__/shell.test.sh
35+
36+
# - name: Setup tmate session
37+
# if: ${{ failure() }}
38+
# uses: mxschmitt/action-tmate@v3
39+
# timeout-minutes: 5
40+
# with:
41+
# limit-access-to-actor: true
42+
test:
43+
runs-on: ubuntu-latest
44+
45+
strategy:
46+
matrix:
47+
node-version: [16.x, 18.x, 20.x]
1648

1749
steps:
1850
- name: Install xsel
@@ -29,9 +61,11 @@ jobs:
2961
xvfb-run npm test
3062
env:
3163
CI: true
64+
NODE_ENV: test
65+
3266
release:
3367
name: semantic-release
34-
needs: [build]
68+
needs: [test, test_no_xsel]
3569
runs-on: ubuntu-latest
3670
outputs:
3771
new-release-published: ${{ steps.semantic-release.outputs.new_release_published }}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,4 @@ jspm_packages
3535

3636
# Optional REPL history
3737
.node_repl_history
38+
dist

.npmignore

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
playground
2+
__tests__
3+
.devcontainer
4+
.github
5+
.vscode
6+
coverage
7+
.all-contributorsrc
8+
.editorconfig
9+
.eslintignore
10+
.eslintrc.cjs
11+
.gitattributes
12+
.gitignore
13+
.prettierrc.cjs
14+
.nvmrc
15+
.jest.config.cjs
16+
lint-staged.config.js
17+
release.config.cjs
18+
tsconfig.json
19+
tsconfig.test.json

.vscode/settings.json

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,31 @@
11
{
22
"workbench.colorCustomizations": {
3+
"activityBar.activeBackground": "#e3a7f9",
34
"activityBar.background": "#e3a7f9",
4-
"activityBar.activeBorder": "#95700a",
55
"activityBar.foreground": "#15202b",
66
"activityBar.inactiveForeground": "#15202b99",
77
"activityBarBadge.background": "#95700a",
88
"activityBarBadge.foreground": "#e7e7e7",
9-
"titleBar.activeBackground": "#d477f6",
10-
"titleBar.inactiveBackground": "#d477f699",
11-
"titleBar.activeForeground": "#15202b",
12-
"titleBar.inactiveForeground": "#15202b99",
13-
"statusBar.background": "#d477f6",
14-
"statusBarItem.hoverBackground": "#c547f3",
15-
"statusBar.foreground": "#15202b",
16-
"activityBar.activeBackground": "#e3a7f9",
179
"commandCenter.border": "#15202b99",
1810
"editorGroup.border": "#e3a7f9",
1911
"panel.border": "#e3a7f9",
2012
"sash.hoverBorder": "#e3a7f9",
2113
"sideBar.border": "#e3a7f9",
14+
"statusBar.background": "#d477f6",
2215
"statusBar.border": "#d477f6",
2316
"statusBar.debuggingBackground": "#99f677",
2417
"statusBar.debuggingBorder": "#99f677",
2518
"statusBar.debuggingForeground": "#15202b",
19+
"statusBar.foreground": "#15202b",
20+
"statusBarItem.hoverBackground": "#c547f3",
2621
"statusBarItem.remoteBackground": "#d477f6",
2722
"statusBarItem.remoteForeground": "#15202b",
2823
"tab.activeBorder": "#e3a7f9",
29-
"titleBar.border": "#d477f6"
24+
"titleBar.activeBackground": "#d477f6",
25+
"titleBar.activeForeground": "#15202b",
26+
"titleBar.border": "#d477f6",
27+
"titleBar.inactiveBackground": "#d477f699",
28+
"titleBar.inactiveForeground": "#15202b99"
3029
},
3130
"peacock.color": "#d477f6",
3231
"spellright.language": [
@@ -37,5 +36,6 @@
3736
"markdown",
3837
"latex",
3938
"plaintext"
40-
]
41-
}
39+
],
40+
"peacock.remoteColor": "#0cab2f"
41+
}

__tests__/parse-flags.test.ts

Lines changed: 119 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,140 @@
1-
import { IParseFlagsOptions } from "./../src/lib/parse-flags";
2-
import * as writer from "../src/lib/write-out";
3-
import clipboardy from "clipboardy";
4-
import { parseFlags } from "../src/lib/parse-flags";
51
import fs from "fs";
2+
import * as clipboardy from "clipboardy";
63

7-
jest.spyOn(process.stdout, "write").mockImplementation();
8-
afterAll(() => {
9-
jest.restoreAllMocks();
10-
});
11-
describe("testing parse flags function", () => {
12-
test("write out call", () => {
13-
const writeOutMock = jest.spyOn(writer, "writeOut");
14-
parseFlags({
15-
data: "<h1>foo</h1>",
16-
inPath: undefined,
17-
outPath: undefined,
18-
});
19-
expect(writeOutMock).toHaveBeenCalledTimes(1);
20-
writeOutMock.mockRestore();
4+
import { IParseFlagsOptions, parseFlags } from "../src/lib/parse-flags";
5+
6+
jest.mock("fs");
7+
8+
describe("parseFlags", () => {
9+
const originalIsTTY = process.stdin.isTTY;
10+
11+
afterEach(() => {
12+
jest.resetAllMocks();
13+
jest.restoreAllMocks();
14+
process.stdin.isTTY = originalIsTTY;
15+
});
16+
17+
it("should return the same data when no inPath or toClipboard options are set", () => {
18+
const options: IParseFlagsOptions = {
19+
data: "test data",
20+
};
21+
22+
const result = parseFlags(options);
23+
expect(result.data).toEqual(options.data);
2124
});
2225

23-
test("input from file", () => {
24-
const mockOut = jest.spyOn(process.stdout, "write");
26+
it("should read data from inPath when inPath is set", () => {
2527
const options: IParseFlagsOptions = {
2628
data: "",
27-
inPath: "__tests__/files/index.html",
29+
inPath: "input.txt",
30+
};
31+
32+
const fileContent = "file content";
33+
(fs.readFileSync as jest.Mock).mockReturnValue(fileContent);
34+
35+
const result = parseFlags(options);
36+
expect(fs.readFileSync).toHaveBeenCalledWith(options.inPath, "utf8");
37+
expect(result.data).toEqual(fileContent);
38+
});
39+
40+
it("should return useGfm as true when useGfm option is set", () => {
41+
const options: IParseFlagsOptions = {
42+
data: "test data",
43+
useGfm: true,
44+
};
45+
46+
const result = parseFlags(options);
47+
expect(result.useGfm).toBe(true);
48+
});
49+
50+
it("should return useGfm as false when useGfm option is not set", () => {
51+
const options: IParseFlagsOptions = {
52+
data: "test data",
2853
};
29-
parseFlags(options);
30-
expect(mockOut.mock.calls[0][0]).toBe("# foo\n");
31-
mockOut.mockRestore();
54+
55+
const result = parseFlags(options);
56+
expect(result.useGfm).toBeUndefined();
3257
});
3358

34-
test("input from file fs read", () => {
35-
const orig = process.stdin.isTTY;
59+
it("should read data from clipboard when toClipboard is set and process.stdin.isTTY is true", async () => {
60+
const options: IParseFlagsOptions = {
61+
data: "",
62+
toClipboard: true,
63+
};
64+
65+
const clipboardContent = "clipboard content";
66+
const clipboardyMock = jest.fn().mockReturnValue(clipboardContent);
67+
3668
process.stdin.isTTY = true;
37-
const mockFsReadSync = jest.spyOn(fs, "readFileSync");
69+
jest.mock("clipboardy", () => {
70+
return {
71+
__esModule: true,
72+
default: {
73+
...clipboardy,
74+
readSync: clipboardyMock,
75+
},
76+
};
77+
});
78+
79+
let parseFlags;
80+
jest.isolateModules(async () => {
81+
parseFlags = require("../src/lib/parse-flags").parseFlags;
82+
await parseFlags(options);
83+
});
84+
85+
expect(clipboardyMock).toHaveBeenCalled();
86+
expect(options.data).toEqual(clipboardContent);
87+
});
88+
89+
it("should not read data from clipboard when process.stdin.isTTY is false", async () => {
3890
const options: IParseFlagsOptions = {
3991
data: "",
40-
inPath: "__tests__/files/index.html",
92+
toClipboard: true,
4193
};
42-
parseFlags(options);
43-
expect(mockFsReadSync).toHaveBeenCalled();
44-
mockFsReadSync.mockRestore();
45-
process.stdin.isTTY = orig;
94+
process.stdin.isTTY = false;
95+
96+
const clipboardyMock = {
97+
readSync: jest.fn(),
98+
};
99+
jest.mock("clipboardy", () => clipboardyMock);
100+
101+
let parseFlags;
102+
await jest.isolateModules(async () => {
103+
parseFlags = require("../src/lib/parse-flags").parseFlags;
104+
await parseFlags(options);
105+
});
106+
107+
process.stdin.isTTY = originalIsTTY;
108+
expect(clipboardyMock.readSync).not.toHaveBeenCalled();
46109
});
47110

48-
test("set and write to clipboard", () => {
49-
const orig = process.stdin.isTTY;
50-
process.stdin.isTTY = true;
51-
jest.spyOn(process, "exit").mockImplementation();
52-
const mockClippy = jest.spyOn(clipboardy, "writeSync");
53-
const data = "<h1>foo</h1>";
54-
clipboardy.writeSync(data);
111+
it("should handle clipboardy.readSync() error and output error message", async () => {
55112
const options: IParseFlagsOptions = {
56113
data: "",
57114
toClipboard: true,
58115
};
59-
parseFlags(options);
60-
expect(mockClippy).toHaveBeenCalled();
61-
expect(clipboardy.readSync()).toBe("# foo\n");
62-
process.stdin.isTTY = orig;
116+
process.stdin.isTTY = true;
117+
const stdoutSpy = jest.spyOn(process.stdout, "write").mockImplementation();
118+
119+
const errorMessage = "Clipboard read error";
120+
121+
await jest.isolateModules(async () => {
122+
jest.mock("clipboardy", () => {
123+
return {
124+
__esModule: true,
125+
default: {
126+
...clipboardy,
127+
readSync: jest.fn().mockImplementation(() => {
128+
throw new Error(errorMessage);
129+
}),
130+
},
131+
};
132+
});
133+
134+
const { parseFlags } = require("../src/lib/parse-flags");
135+
136+
parseFlags(options);
137+
expect(stdoutSpy).toHaveBeenCalledWith(`${errorMessage}\n`);
138+
});
63139
});
64140
});

__tests__/shell.test.sh

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/usr/bin/env bash
2+
# shellcheck disable=SC2016
3+
set -u pipefail
4+
IFS=$'\n\t'
5+
output=$(echo "<h1>foo</h1>" | html2md)
6+
exitcode=1
7+
if echo "$output" | grep -q "# foo"; then
8+
echo "Test passed"
9+
echo "$output"
10+
exitcode=0
11+
else
12+
echo "Test failed"
13+
echo "$output"
14+
exitcode=1
15+
fi
16+
output=$(html2md -c)
17+
if echo "$output" | grep -q 'Couldn'\''t find the `xsel` binary and fallback didn'\''t work. On Debian/Ubuntu you can install xsel with: sudo apt install xsel'; then
18+
echo "Test passed xsel"
19+
echo "$output"
20+
exitcode=0
21+
else
22+
echo "Test failed xsel"
23+
echo "$output"
24+
exitcode=1
25+
fi
26+
exit $exitcode

0 commit comments

Comments
 (0)