page2pdf_server 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +3 -0
- package/.eslintrc +33 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +28 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +15 -0
- package/.github/ISSUE_TEMPLATE/refactoring.md +15 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +18 -0
- package/.github/stale.yml +17 -0
- package/.github/workflows/cd.yml +75 -0
- package/.github/workflows/ci.yml +36 -0
- package/.husky/pre-commit +6 -0
- package/.husky/pre-push +4 -0
- package/.idea/codeStyles/Project.xml +58 -0
- package/.idea/codeStyles/codeStyleConfig.xml +5 -0
- package/.idea/encodings.xml +7 -0
- package/.idea/inspectionProfiles/Project_Default.xml +7 -0
- package/.idea/modules.xml +8 -0
- package/.idea/page2pdf-server.iml +12 -0
- package/.idea/tenstack-starter-main.iml +12 -0
- package/.idea/vcs.xml +6 -0
- package/.prettierrc +8 -0
- package/.vscode/settings.json +3 -0
- package/LICENSE +18 -0
- package/README.md +238 -0
- package/__tests__/UrltoPdf/generatePdf.test.ts +207 -0
- package/__tests__/UrltoPdf/pdfSplit.test.ts +69 -0
- package/__tests__/helpers/index.ts +21 -0
- package/__tests__/home.test.ts +77 -0
- package/config/default.json +10 -0
- package/config/development.json +3 -0
- package/config/production.json +3 -0
- package/config/test.json +3 -0
- package/ecosystem.config.js +41 -0
- package/eslintrc.json +14 -0
- package/jest.config.js +35 -0
- package/nodemon.json +6 -0
- package/package.json +105 -0
- package/src/CSS/345/205/274/345/256/271/346/200/247.txt +56 -0
- package/src/app.ts +41 -0
- package/src/components/home/controller.ts +27 -0
- package/src/components/home/index.ts +4 -0
- package/src/components/home/pdfController.ts +112 -0
- package/src/components/home/services.ts +31 -0
- package/src/components/home/splitController.ts +124 -0
- package/src/components/home/validators.ts +12 -0
- package/src/configEnv/index.ts +62 -0
- package/src/db/home.ts +14 -0
- package/src/helpers/apiResponse.ts +10 -0
- package/src/helpers/dataSanitizers.ts +31 -0
- package/src/helpers/error/ApiError.ts +25 -0
- package/src/helpers/error/ForbiddenError.ts +15 -0
- package/src/helpers/error/NotFoundException.ts +15 -0
- package/src/helpers/error/TimeOutError.ts +20 -0
- package/src/helpers/error/UnauthorizedError.ts +15 -0
- package/src/helpers/error/ValidationError.ts +20 -0
- package/src/helpers/error/index.ts +15 -0
- package/src/helpers/index.ts +2 -0
- package/src/helpers/loggers.ts +73 -0
- package/src/index.ts +13 -0
- package/src/middlewares/errorHandler.ts +52 -0
- package/src/new_tab1.mhtml +722 -0
- package/src/routes/index.ts +22 -0
- package/src/server.ts +30 -0
- package/src/testCSS.html +241 -0
- package/src/types/global.d.ts +13 -0
- package/src/types/request/home.ts +166 -0
- package/src/types/request/split.ts +18 -0
- package/src/types/response/AppInformation.ts +9 -0
- package/src/types/response/index.ts +5 -0
- package/src/utils/array.ts +19 -0
- package/src/utils/auth.ts +12 -0
- package/src/utils/crypt.ts +26 -0
- package/src/utils/filter.ts +59 -0
- package/src/utils/object.ts +58 -0
- package/src/utils/pdfgen.ts +998 -0
- package/src/utils/url.ts +54 -0
- package/src//346/265/213/350/257/225.txt +241 -0
- package/tsconfig.json +41 -0
- package/tslint.json +22 -0
- package//346/226/207/344/271/246/346/211/223/345/215/260/350/275/254/346/215/242/345/231/250.bat +2 -0
package/README.md
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img width="250px" src="https://user-images.githubusercontent.com/50701501/104827248-f88a1800-585b-11eb-985e-5e31dbb0b913.jpg"><br/>
|
|
3
|
+
</p>
|
|
4
|
+
<p align="center">
|
|
5
|
+
<a href="https://lerna.js.org/"><img src="https://img.shields.io/badge/PRs-Welcome-brightgreen.svg" alt="Maintained with Lerna"></a>
|
|
6
|
+
<a href="/LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License"></a>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
# Overview
|
|
10
|
+
|
|
11
|
+
**page2pdf_server** is a `Typescript` + `Express` + `Node` starter kit to develop `REST API` server apps.
|
|
12
|
+
Nothing new under the sun, just a straight forward combo to make server development a little bit faster. And of course, this make my freelancing days more enjoyable 😎
|
|
13
|
+
Comes with:
|
|
14
|
+
|
|
15
|
+
- Everything typed with [Typescript](https://www.typescriptlang.org/)
|
|
16
|
+
- [ES6](http://babeljs.io/learn-es2015/) features/modules
|
|
17
|
+
- ES7 [async](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) / [await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await)
|
|
18
|
+
- Run with [Nodemon](https://nodemon.io/) for automatic reload & watch
|
|
19
|
+
- [ESLint](http://eslint.org/) for code linting
|
|
20
|
+
- Code formatting using [Prettier](https://www.npmjs.com/package/prettier)
|
|
21
|
+
- Configuration management using [dotenv](https://www.npmjs.com/package/dotenv)
|
|
22
|
+
- Improved commits with [Husky](https://typicode.github.io/husky)
|
|
23
|
+
- Manage production app proccess with [PM2](https://pm2.keymetrics.io/)
|
|
24
|
+
|
|
25
|
+
<br>
|
|
26
|
+
<br>
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
## 由来
|
|
30
|
+
目前动态生成的网页html在浏览器上直接打印时,不能支持纸张的横向或纵向方向的混合一次性的打印输出,当前做法只能拆开为多个的打印命令分别打印后再合并装订。
|
|
31
|
+
浏览器不同版本打印网页的效果稍有一点点差距就会导致文书打印时的页码目录针对的页号出现预先设想的页号和实际打印结果不一致性,这对于有些要求苛刻的文书打印,
|
|
32
|
+
动态网页直接打印明显处于不适应地位。为了缝合这个缺陷,特别地推出本软件功能。
|
|
33
|
+
原本猜想wkhtmltopdf可以,但是发现wkhtmltopdf对于动态生成的网页有js css的普通APP页面生成的效果不如谷歌浏览器好,常用的html2canvas+jsPDF,
|
|
34
|
+
html2pdf.js方案以及更为原始的PDFKit方案都无法完美解决和浏览器的打印预览效果差异大的问题。chrome-remote-interface是比Puppeteer的API更底层的库。
|
|
35
|
+
chrome-remote-interface不会为您启动Chrome。本来想用chrome-remote-interface在纯粹的浏览器客户机环境下实现,但发现可能权限的理由导致尝试失败,
|
|
36
|
+
认定它和puppeteer一样都只能在node.js环境下有效,chrome-remote-interface比起puppeteer更为底层的。
|
|
37
|
+
本软件定名“文书打印转换器” page2pdf_server,本质是个node.js的web服务器,但实际只提供给本机使用,也不支持多个用户一起用的,web客户端也就是其它通用的
|
|
38
|
+
前端APP浏览器客户使用http url访问本服务模块,最终在本地主机目录生成pdf文件以供进一步打印或发送邮件或移走pdf文件。可支持SPA APP动态生成的网页多个链接
|
|
39
|
+
的合并输出,但前提要求这些网页URL在打印信号前必须安静下来亦即准备好视图,后续不会再执行脚本语言修改页面了。封面cover和目录TOC这些也需要应用层APP自己负责
|
|
40
|
+
去生成和校订页码的,不支持pdf的内部点击链接跳转显示页的能力,目标是直接给打印机打印使用的过渡性质的pdf,若要此类能力请直接回到APP动态网页的前端浏览去。
|
|
41
|
+
后续的真实打印机输出,请找ipp实现连接打印机后端静默打印html文件,pdf放入前端预览看react-file-preview-latest嵌入文档阅读器。
|
|
42
|
+
注意,动态网页在Chrome浏览器直接打印预览的效果应该和本软件转换输出的pdf一致效果,为打印设置的css必须在应用层中自己去添加的。
|
|
43
|
+
纯粹静态html的打印转换pdf,且是标准纸张大小的横竖纸张方向掺杂的这种情况:考虑一次提取一部分pageRanges[]吗,分解多个的files[url,out]独立生成之后做合并。
|
|
44
|
+
|
|
45
|
+
## Prerequisites
|
|
46
|
+
|
|
47
|
+
- [Node.js](https://nodejs.org) (`>= 18.0.0`)
|
|
48
|
+
- [Yarn](https://yarnpkg.com/en/docs/install) or [NPM](https://docs.npmjs.com/getting-started/installing-node)
|
|
49
|
+
- 谷歌浏览器,要配置端口号,启动命令设置 C:\Users\herzhang\AppData\Local\Google\Chrome\Application\chrome.exe --remote-debugging-port=9872
|
|
50
|
+
|
|
51
|
+
## Install
|
|
52
|
+
|
|
53
|
+
- Fork or Use [this](https://github.com/filoscoder/tenstack-starter/generate) template repository.
|
|
54
|
+
- [Clone](https://github.com/git-guides/git-clone) the forked repository.
|
|
55
|
+
- Install the dependencies with [yarn](https://yarnpkg.com/getting-started/usage) or [npm](https://docs.npmjs.com/cli/v7/commands/npm-install).
|
|
56
|
+
|
|
57
|
+
> Make sure you already have [`node.js`](https://github.com/filoscoder/tenstack-starter#prerequisites) and [`npm`](https://github.com/filoscoder/tenstack-starter#prerequisites) or [`yarn`](https://github.com/filoscoder/tenstack-starter#prerequisites) installed in your system.
|
|
58
|
+
|
|
59
|
+
- Set your `git remote add origin` path
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
git remote add origin ${forked-and-cloned-path}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
> [Update the url](https://docs.github.com/en/get-started/getting-started-with-git/managing-remote-repositories#changing-a-remote-repositorys-url) if you already have an `origin`
|
|
66
|
+
|
|
67
|
+
<br>
|
|
68
|
+
<br>
|
|
69
|
+
|
|
70
|
+
## Config
|
|
71
|
+
|
|
72
|
+
- Copy `.env.example` a file at the root of the application.
|
|
73
|
+
- Add or modify specific variables and update it according to your need.
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
cp .env.example .env
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
> Check the `config` folder to customize your settings (`/src/config`)
|
|
80
|
+
|
|
81
|
+
<br>
|
|
82
|
+
<br>
|
|
83
|
+
|
|
84
|
+
## Alias @
|
|
85
|
+
|
|
86
|
+
To make paths clean and ease to access `@` is setup up for `/src` path
|
|
87
|
+
|
|
88
|
+
```javascript
|
|
89
|
+
// BEFORE
|
|
90
|
+
import config from './config';
|
|
91
|
+
import routes from './routes';
|
|
92
|
+
|
|
93
|
+
// NOW
|
|
94
|
+
import config from '@/config';
|
|
95
|
+
import routes from '@/routes';
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
> You can customize this setup:
|
|
99
|
+
> `/tsconfig.json` > compilerOptions.paths
|
|
100
|
+
> `/eslintrc.yml` > rules.settings.alias.map
|
|
101
|
+
|
|
102
|
+
<br>
|
|
103
|
+
<br>
|
|
104
|
+
|
|
105
|
+
## Local Development
|
|
106
|
+
|
|
107
|
+
Run the server locally. It will be run with Nodemon and ready to serve on port `8080` (unless you specify it on your `.env`)
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
yarn start # or npm start
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
> Check [`package.json`](https://github.com/filoscoder/tenstack-starter/blob/master/package.json) to see more "scripts"
|
|
114
|
+
|
|
115
|
+
<br>
|
|
116
|
+
<br>
|
|
117
|
+
|
|
118
|
+
## Production
|
|
119
|
+
|
|
120
|
+
First, build the application.
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
yarn build # or npm run build
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Then, use [`pm2`](https://github.com/Unitech/pm2) to start the application as a service.
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
yarn service:start # or npm run service:start
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
<br>
|
|
133
|
+
<br>
|
|
134
|
+
|
|
135
|
+
# Contribution
|
|
136
|
+
|
|
137
|
+
This repository will be managed as an `open-source`. <br>
|
|
138
|
+
Please feel free to open an `issue` or a `pull request` to suggest changes or additions.
|
|
139
|
+
|
|
140
|
+
# Support & Contact
|
|
141
|
+
|
|
142
|
+
If you have any question or suggestion, don't hesitate to contact me:
|
|
143
|
+
|
|
144
|
+
✉️ [herzhang@163.com](mailto:herzhang@163.com)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
# Author & Credits
|
|
148
|
+
|
|
149
|
+
<a src="https://gitee.com/heerzhang/page2pdf-server">
|
|
150
|
+
<img width="60px" style="border-radius: 50%;" src="https://foruda.gitee.com/avatar/1677151822424320339/8832894_heerzhang_1616223106.png!avatar30">
|
|
151
|
+
</a>
|
|
152
|
+
|
|
153
|
+
# windows驻守长期运行本服务,纯粹服务端模式
|
|
154
|
+
node.js必须有安装。执行底下命令:
|
|
155
|
+
npm install pm2 -g
|
|
156
|
+
npm install pm2-windows-startup -g
|
|
157
|
+
若运行 pm2-startup install 报错的
|
|
158
|
+
在系统中搜索Windows PowerShell,用管理员身份打开
|
|
159
|
+
打开窗口输入命令行 set-ExecutionPolicy RemoteSigned 输入y回车确认,再回头运行pm2-startup install。
|
|
160
|
+
pm2 start D:\home\page2pdf-server\dist\src\index.js --name page2pdf-server
|
|
161
|
+
pm2 save #保证重启电脑可以自动跑
|
|
162
|
+
pm2 ls #查看守护服务情况,自带进程node C:\Users\herzhang\AppData\Roaming\npm\node_modules\pm2\lib\Daemon.js
|
|
163
|
+
pm2 stop all # 停止所有服务进程
|
|
164
|
+
pm2 delete 0 # 删除服务(pm2 delete app_id)
|
|
165
|
+
要卸载服务,执行:pm2-service-uninstall 直接杀掉自带进程node;
|
|
166
|
+
pm2 logs — 从所有正在运行的应用中输出日志
|
|
167
|
+
pm2 logs page2pdf-server — 只从指定的应用中输出日志
|
|
168
|
+
pm2 flush — 刷新所有日志数据,释放磁盘空间
|
|
169
|
+
# 前台可看得见输出的运行模式,windows环境的
|
|
170
|
+
npm install -g nodemon
|
|
171
|
+
start nodemon D:\home\page2pdf-server\dist\src\index.js
|
|
172
|
+
桌面上整个 文书打印转换器.bat 双击运行。
|
|
173
|
+
文书打印转换器.bat 命令cmd窗口可能挂起,注意回撤继续。
|
|
174
|
+
cmd命令行窗口鼠标点击后挂起的原因和处理方法;https://blog.csdn.net/fanstering/article/details/116455669
|
|
175
|
+
注意Chrome浏览器启动cmd配置,chrome.exe --headless不能加,不然报错: No inspectable targets。
|
|
176
|
+
|
|
177
|
+
# 前端对接和使用API
|
|
178
|
+
http://localhost:9389/api/pdf
|
|
179
|
+
前端发送请求后,可观察浏览器页面以及本地工作目录生成文件和本文书打印转换器的终端输出,前端APP最好发起打印转换指令以后等待结果完成,
|
|
180
|
+
当然也允许不等待的,直接从转换器来获得结果输出,前端继续干别的事,但是应该保证本转换器只会单用户单进程排他的运作,不要同时进行多个转换命令。
|
|
181
|
+
要测试,用IDEA的HTTP客户端发送如下:
|
|
182
|
+
POST http://localhost:9389/api/pdf
|
|
183
|
+
Content-Type: application/json
|
|
184
|
+
|
|
185
|
+
{
|
|
186
|
+
"merge": true,
|
|
187
|
+
"name": "asjsak啊实打实",
|
|
188
|
+
"lay": {
|
|
189
|
+
"head": "address"
|
|
190
|
+
},
|
|
191
|
+
"files": [
|
|
192
|
+
{
|
|
193
|
+
"url": "ChromePage7.pdf",
|
|
194
|
+
"out": "add_ChromePag"
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"url": "https://www.npmjs.com/search?q=chrome-remote-interface",
|
|
198
|
+
"out": "横页脚的qa"
|
|
199
|
+
}
|
|
200
|
+
]
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
注意,如果发送post请求的body底下的内容实际为空的话,本转换器默认执行:把工作目录的所有pdf文件按照修改时间顺序进行合并,默认输出文件名=配置好的名字。
|
|
204
|
+
假若某个URL不正常的,很可能无法触发页面加载完成,导致作业一致无法继续下去,大大超出预期执行时间。URL对应网页若不是一次性加载完成还会陆陆续续修改内容的就是这种情况造成死等长时间没结果。
|
|
205
|
+
|
|
206
|
+
# 页眉页脚配置html脚本转为json后
|
|
207
|
+
例子,用https://uutool.cn/html2json/ 工具可转换,转换才能通过api/pdf发送命令的。
|
|
208
|
+
"lay": {
|
|
209
|
+
"head": [
|
|
210
|
+
"<div style=\\\"position: relative; width:100%; text-align:center; border-bottom: 1pt solid #eeeeee; margin: 3.5mm 0px 10px; font-size: 10pt\\\"></span>",
|
|
211
|
+
"<div style=\\\"position: absolute; width:100%; text-align: center; bottom: 5px;\\\"><span class=title></div>",
|
|
212
|
+
"<div style=\\\"position: absolute; text-align: right; bottom: 5px;right: 20px;\\\">version: 1.0</div>",
|
|
213
|
+
"</div>"
|
|
214
|
+
] ,
|
|
215
|
+
"foot": [
|
|
216
|
+
"<div style=\\\"position: relative; width: 100%; text-align: left; border-top: 1pt solid #eeeeee; margin: 10px 0px 1.5mm; font-size: 8pt;\\\">",
|
|
217
|
+
"<div style=\\\"position: absolute; text-align: left; top: 5px;left: 60px;\\\">YYYY-MM-DD</div>",
|
|
218
|
+
"<div style=\\\"position: absolute; width: 100%; text-align: center; top: 5px;\\\">©2022 ABCD</div>",
|
|
219
|
+
"<div style=\\\"position: absolute; text-align: right;top: 5px;right: 20px;\\\"> <span class=pageNumber></span> / <span class=totalPages></span></div>",
|
|
220
|
+
"</div>"
|
|
221
|
+
]
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
# 注意适用性
|
|
225
|
+
本文书打印转换器只能适用单用户在客户本机用,每次执行一个打印指令,不要并行发出指令,本机工作目录也是临时文件,针对当前指令适用的,不能存放无关文件。
|
|
226
|
+
本转换器console命令输出窗口可实时观察到当前打印转换指令的执行过程输出提示,同时目标浏览器也会新开启浏览窗口,这些由本转换器自动开启的浏览窗口实际上
|
|
227
|
+
就是转换读取页面的过程,若无法访问页面或没有登录目标url网站的就能体现在这些窗口中,每一个url转换生成了相应对应pdf之后就失去用处了。
|
|
228
|
+
本转换器内部使用了CDP协议Chrome DevTools Protocol连接的浏览器进程,暂时没发现安全性问题。
|
|
229
|
+
# 配置文件
|
|
230
|
+
本地较为固定的参数设置,在/config/default.json文件,例子如下的,注意自定义纸张没有方向的说法,"w"宽度,"h"高度,单位英寸。
|
|
231
|
+
{
|
|
232
|
+
"base_path": "E:\\临时\\web打印\\pdfs",
|
|
233
|
+
"default_file": "合并后的文档",
|
|
234
|
+
"size":[
|
|
235
|
+
{"name":"D8", "css": "4in 6in","w": 4,"h": 6},
|
|
236
|
+
{"name":"D4", "css": "10.4in 12.6in","w": 10.4,"h": 12.6}
|
|
237
|
+
]
|
|
238
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { SuperAgentTest } from "supertest";
|
|
2
|
+
import { OK } from "http-status/lib";
|
|
3
|
+
import { initAgent } from "../helpers";
|
|
4
|
+
|
|
5
|
+
//prettify真叼: 每个地方都给您报出 红色
|
|
6
|
+
const TIMEOUT = 260000; //设置超时,避免单步歇菜
|
|
7
|
+
let agent: SuperAgentTest;
|
|
8
|
+
|
|
9
|
+
beforeAll(async () => {
|
|
10
|
+
agent = await initAgent();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
/**替代: 浏览器postman等工具发送包后,后端服务器Debug模式运行单步调试模式已经没能使用了。只好改为jest来对付了。
|
|
14
|
+
后面的/${CONFIG.APP.VER} 就是加了/v1结尾的该服务工程的版本号;
|
|
15
|
+
* */
|
|
16
|
+
describe("[PDF] => Gen", () => {
|
|
17
|
+
describe("Post: '/api/pdf'", () => {
|
|
18
|
+
it(
|
|
19
|
+
"通用基本的",
|
|
20
|
+
async () => {
|
|
21
|
+
const response = await agent.post(`/api/pdf`).send({
|
|
22
|
+
name: "asjsak啊实打实",
|
|
23
|
+
lay: {
|
|
24
|
+
head: [
|
|
25
|
+
'<div style=\\"position: relative; width:100%; text-align:center; border-bottom: 1pt solid #eeeeee; margin: 3.5mm 0px 10px; font-size: 10pt\\"></span>',
|
|
26
|
+
'<div style=\\"position: absolute; width:100%; text-align: center; bottom: 5px;\\"><span class=title></div>',
|
|
27
|
+
'<div style=\\"position: absolute; text-align: right; bottom: 5px;right: 20px;\\">version: 1.0</div>',
|
|
28
|
+
"</div>",
|
|
29
|
+
],
|
|
30
|
+
foot: [
|
|
31
|
+
'<div style=\\"position: relative; width: 100%; text-align: left; border-top: 1pt solid #eeeeee; margin: 10px 0px 1.5mm; font-size: 8pt;\\">',
|
|
32
|
+
'<div style=\\"position: absolute; text-align: left; top: 5px;left: 60px;\\">YYYY-MM-DD</div>',
|
|
33
|
+
'<div style=\\"position: absolute; width: 100%; text-align: center; top: 5px;\\">©2022 ABCD</div>',
|
|
34
|
+
'<div style=\\"position: absolute; text-align: right;top: 5px;right: 20px;\\"> <span>${pageNumber}</span> / <span>${totalPages}</span></div>',
|
|
35
|
+
"</div>",
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
files: [
|
|
39
|
+
{
|
|
40
|
+
url: "testHtmlPrint.html",
|
|
41
|
+
out: "testHtmlPrint转",
|
|
42
|
+
headFrom: 4,
|
|
43
|
+
frNo: 0,
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
url: "横页脚的.pdf",
|
|
47
|
+
displayHeaderFooter: true,
|
|
48
|
+
rightHand: true,
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
});
|
|
52
|
+
expect(response.status).toBe(OK);
|
|
53
|
+
expect(Object.keys(response.body.data)).toEqual(["result"]);
|
|
54
|
+
},
|
|
55
|
+
TIMEOUT,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
it(
|
|
59
|
+
"汇总阶段第二个url独立pdf输出",
|
|
60
|
+
async () => {
|
|
61
|
+
const response = await agent.post(`/api/pdf`).send({
|
|
62
|
+
name: "asjsak啊实打实",
|
|
63
|
+
lay: {
|
|
64
|
+
head: [
|
|
65
|
+
'<div style=\\"position: relative; width:100%; text-align:center; border-bottom: 1pt solid #eeeeee; margin: 3.5mm 0px 10px; font-size: 10pt\\"></span>',
|
|
66
|
+
'<div style=\\"position: absolute; width:100%; text-align: center; bottom: 5px;\\"><span class=title></div>',
|
|
67
|
+
'<div style=\\"position: absolute; text-align: right; bottom: 5px;right: 20px;\\">version: 1.0</div>',
|
|
68
|
+
"</div>",
|
|
69
|
+
],
|
|
70
|
+
foot: [
|
|
71
|
+
'<div style=\\"position: relative; width: 100%; text-align: left; border-top: 1pt solid #eeeeee; margin: 10px 0px 1.5mm; font-size: 8pt;\\">',
|
|
72
|
+
'<div style=\\"position: absolute; text-align: left; top: 5px;left: 60px;\\">YYYY-MM-DD</div>',
|
|
73
|
+
'<div style=\\"position: absolute; width: 100%; text-align: center; top: 5px;\\">©2022 ABCD</div>',
|
|
74
|
+
'<div style=\\"position: absolute; text-align: right;top: 5px;right: 20px;\\"> <span>${pageNumber}</span> / <span>${totalPages}</span></div>',
|
|
75
|
+
"</div>",
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
files: [
|
|
79
|
+
{
|
|
80
|
+
url: "testHtmlPrint.html",
|
|
81
|
+
out: "testHtmlPrint转",
|
|
82
|
+
lay: {
|
|
83
|
+
marginTop: 0.55,
|
|
84
|
+
marginBottom: 0.45,
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
url: "横页脚的.pdf",
|
|
89
|
+
merge: false,
|
|
90
|
+
displayHeaderFooter: true,
|
|
91
|
+
rightHand: true,
|
|
92
|
+
lay: {
|
|
93
|
+
marginTop: 0.55,
|
|
94
|
+
marginBottom: 0.45,
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
});
|
|
99
|
+
expect(response.status).toBe(OK);
|
|
100
|
+
expect(Object.keys(response.body.data)).toEqual(["result"]);
|
|
101
|
+
},
|
|
102
|
+
TIMEOUT,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
it(
|
|
106
|
+
"双面打印的页码_总的",
|
|
107
|
+
async () => {
|
|
108
|
+
const response = await agent.post(`/api/pdf`).send({
|
|
109
|
+
name: "asjsak啊实打实",
|
|
110
|
+
lay: {
|
|
111
|
+
head: [
|
|
112
|
+
'<div style=\\"position: relative; width:100%; text-align:center; border-bottom: 1pt solid #eeeeee; margin: 3.5mm 0px 10px; font-size: 10pt\\"></span>',
|
|
113
|
+
'<div style=\\"position: absolute; width:100%; text-align: center; bottom: 5px;\\"><span id=\\"titlespan\\" class=title></div>',
|
|
114
|
+
'<div style=\\"-NOT_DISPLAY- position: absolute; text-align: right; bottom: 5px;right: 20px;\\">',
|
|
115
|
+
"<span>${pageNumber}</span> / <span>${totalPages}</span></div>",
|
|
116
|
+
"</div>",
|
|
117
|
+
],
|
|
118
|
+
foot: [
|
|
119
|
+
'<div style=\\"position: relative; width: 100%; text-align: left; border-top: 1pt solid #eeeeee; margin: 10px 0px 1.5mm; font-size: 8pt;\\">',
|
|
120
|
+
'<div style=\\"position: absolute; text-align: left; top: 5px;left: 60px;\\">YYYY-MM-DD</div>',
|
|
121
|
+
'<div style=\\"position: absolute; width: 100%; text-align: center; top: 5px;\\">©2022 ABCD</div>',
|
|
122
|
+
'<div style=\\"position: absolute; text-align: right;top: 5px;right: 20px;\\"> <span>${pageNumber}</span> / <span>${totalPages}</span></div>',
|
|
123
|
+
"</div>",
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
files: [
|
|
127
|
+
{
|
|
128
|
+
url: "testHtmlPrint.html",
|
|
129
|
+
out: "testHtmlPrint转",
|
|
130
|
+
headFrom: 4,
|
|
131
|
+
frNo: 5,
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
url: "横页脚的.pdf",
|
|
135
|
+
displayHeaderFooter: true,
|
|
136
|
+
rightHand: true,
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
});
|
|
140
|
+
expect(response.status).toBe(OK);
|
|
141
|
+
expect(Object.keys(response.body.data)).toEqual(["result"]);
|
|
142
|
+
},
|
|
143
|
+
TIMEOUT,
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
it(
|
|
147
|
+
"双面打印的页码_单URL",
|
|
148
|
+
async () => {
|
|
149
|
+
const response = await agent.post(`/api/pdf`).send({
|
|
150
|
+
name: "asjsak啊实打实",
|
|
151
|
+
lay: {},
|
|
152
|
+
files: [
|
|
153
|
+
{
|
|
154
|
+
url: "report.pdf",
|
|
155
|
+
out: "热效率详细测试",
|
|
156
|
+
headFrom: 4,
|
|
157
|
+
frNo: 5,
|
|
158
|
+
localSumNo: true,
|
|
159
|
+
roman: true,
|
|
160
|
+
lay: {
|
|
161
|
+
head: [
|
|
162
|
+
'<div style=\\"position: relative; width:100%; text-align:center; border-bottom: 1pt solid #eeeeee; margin: 3.5mm 0px 10px; font-size: 10pt\\"></span>',
|
|
163
|
+
'<div style=\\"position: absolute; width:100%; text-align: center; bottom: 5px;\\"><span id=\\"titlespan\\" class=title></div>',
|
|
164
|
+
'<div style=\\"-NOT_DISPLAY- position: absolute; text-align: right; bottom: 5px;right: 20px;\\">',
|
|
165
|
+
"<span>${pageNumber}</span> / <span>${totalPages}</span></div>",
|
|
166
|
+
"</div>",
|
|
167
|
+
],
|
|
168
|
+
foot: [
|
|
169
|
+
'<div style=\\"position: relative; width: 100%; text-align: left; border-top: 1pt solid #eeeeee; margin: 10px 0px 1.5mm; font-size: 8pt;\\">',
|
|
170
|
+
'<div style=\\"position: absolute; text-align: left; top: 5px;left: 60px;\\">YYYY-MM-DD</div>',
|
|
171
|
+
'<div style=\\"position: absolute; width: 100%; text-align: center; top: 5px;\\">©2022 ABCD</div>',
|
|
172
|
+
'<div style=\\"-NOT_DISPLAY- position: absolute; text-align: right;top: 5px;right: 20px;\\"> <span>${pageNumber}</span> / <span>${totalPages}</span></div>',
|
|
173
|
+
"</div>",
|
|
174
|
+
],
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
url: "横页脚的.pdf",
|
|
179
|
+
displayHeaderFooter: true,
|
|
180
|
+
rightHand: true,
|
|
181
|
+
lay: {
|
|
182
|
+
head: [
|
|
183
|
+
'<div style=\\"position: relative; width:100%; text-align:center; border-bottom: 1pt solid #eeeeee; margin: 3.5mm 0px 10px; font-size: 10pt\\"></span>',
|
|
184
|
+
'<div style=\\"position: absolute; width:100%; text-align: center; bottom: 5px;\\"><span id=\\"titlespan\\" class=title></div>',
|
|
185
|
+
'<div style=\\"-NOT_DISPLAY- position: absolute; text-align: right; bottom: 5px;right: 20px;\\">',
|
|
186
|
+
"<span>${pageNumber}</span> / <span>${totalPages}</span></div>",
|
|
187
|
+
"</div>",
|
|
188
|
+
],
|
|
189
|
+
foot: [
|
|
190
|
+
'<div style=\\"position: relative; width: 100%; text-align: left; border-top: 1pt solid #eeeeee; margin: 10px 0px 1.5mm; font-size: 8pt;\\">',
|
|
191
|
+
'<div style=\\"position: absolute; text-align: left; top: 5px;left: 60px;\\">YYYY-MM-DD</div>',
|
|
192
|
+
'<div style=\\"position: absolute; width: 100%; text-align: center; top: 5px;\\">©2022 ABCD</div>',
|
|
193
|
+
'<div style=\\"-NOT_DISPLAY- position: absolute; text-align: right;top: 5px;right: 20px;\\"> <span>${pageNumber}</span> / <span>${totalPages}</span></div>',
|
|
194
|
+
"</div>",
|
|
195
|
+
],
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
});
|
|
200
|
+
expect(response.status).toBe(OK);
|
|
201
|
+
expect(Object.keys(response.body.data)).toEqual(["result"]);
|
|
202
|
+
console.log("结果:", response.body.data);
|
|
203
|
+
},
|
|
204
|
+
TIMEOUT,
|
|
205
|
+
);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { SuperAgentTest } from "supertest";
|
|
2
|
+
import { OK } from "http-status/lib";
|
|
3
|
+
import { initAgent } from "../helpers";
|
|
4
|
+
// import CONFIG from "../../src/configEnv";
|
|
5
|
+
|
|
6
|
+
//prettify真叼: 每个地方都给您报出 红色
|
|
7
|
+
const TIMEOUT = 260000; //设置超时,避免单步歇菜
|
|
8
|
+
let agent: SuperAgentTest;
|
|
9
|
+
|
|
10
|
+
beforeAll(async () => {
|
|
11
|
+
agent = await initAgent();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
/**替代: 浏览器postman等工具发送包后,后端服务器Debug模式运行单步调试模式已经没能使用了。只好改为jest来对付了。
|
|
15
|
+
后面的/${CONFIG.APP.VER} 就是加了/v1结尾的该服务工程的版本号;
|
|
16
|
+
* */
|
|
17
|
+
describe("[PDF] => Gen", () => {
|
|
18
|
+
describe("Post: '/api/pdf'", () => {
|
|
19
|
+
it(
|
|
20
|
+
"全部合并工作目录的所有pdf文件",
|
|
21
|
+
async () => {
|
|
22
|
+
const response = await agent.post(`/api/pdf`).send({});
|
|
23
|
+
expect(response.status).toBe(OK);
|
|
24
|
+
expect(Object.keys(response.body.data)).toEqual(["result"]);
|
|
25
|
+
},
|
|
26
|
+
TIMEOUT,
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
it(
|
|
30
|
+
"pdf建议拆分成多个pdf",
|
|
31
|
+
async () => {
|
|
32
|
+
const response = await agent.post(`/api/split`).send({
|
|
33
|
+
input: "产生的22.pdf",
|
|
34
|
+
files: [
|
|
35
|
+
{
|
|
36
|
+
pageRanges: "1-3,7-8",
|
|
37
|
+
out: "拆分achaRErint转",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
pageRanges: "7,6,1",
|
|
41
|
+
out: "最小那3QWDS",
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
});
|
|
45
|
+
expect(response.status).toBe(OK);
|
|
46
|
+
expect(Object.keys(response.body.data)).toEqual(["result"]);
|
|
47
|
+
expect(response.body.data).toEqual({ result: "成功!" });
|
|
48
|
+
console.log("结果:", response.body.data);
|
|
49
|
+
},
|
|
50
|
+
TIMEOUT,
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
/*这里发送的和IDEA工具发送差异: .post(`/api/split`).send({})是对象化的。
|
|
56
|
+
{
|
|
57
|
+
"input": "产生的22.pdf",
|
|
58
|
+
"files": [
|
|
59
|
+
{
|
|
60
|
+
"pageRanges": "1-3,5,8",
|
|
61
|
+
"out": "ch拆分achaRErint转"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"pageRanges": "4,8",
|
|
65
|
+
"out": "tesrint2323QWDS"
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
* */
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import supertest, { SuperAgentTest } from "supertest";
|
|
2
|
+
|
|
3
|
+
import { createApp } from "@/app";
|
|
4
|
+
|
|
5
|
+
// let dbConnection: Mongoose;
|
|
6
|
+
|
|
7
|
+
export const initAgent = async (): Promise<SuperAgentTest> => {
|
|
8
|
+
const app = createApp();
|
|
9
|
+
const agent = supertest.agent(app);
|
|
10
|
+
// dbConnection = await mongoDbConnection();
|
|
11
|
+
// await agent.post("/api/v2/auth/signin").send({
|
|
12
|
+
// email: testUser.email,
|
|
13
|
+
// password: testUser.password,
|
|
14
|
+
// });
|
|
15
|
+
|
|
16
|
+
return agent;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// export const closeClients = async (): Promise<void> => {
|
|
20
|
+
// await dbConnection.disconnect();
|
|
21
|
+
// };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { SuperAgentTest } from "supertest";
|
|
2
|
+
import HttpStatus, { OK, BAD_REQUEST } from "http-status/lib";
|
|
3
|
+
import { initAgent } from "./helpers";
|
|
4
|
+
import CONFIG from "@/configEnv";
|
|
5
|
+
|
|
6
|
+
//prettify真叼: 每个地方都给您报出 红色
|
|
7
|
+
const TIMEOUT = 60000; //设置超时,避免单步歇菜
|
|
8
|
+
let agent: SuperAgentTest;
|
|
9
|
+
|
|
10
|
+
beforeAll(async () => {
|
|
11
|
+
agent = await initAgent();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe("[UNIT] => HOME", () => {
|
|
15
|
+
describe("GET: '/'", () => {
|
|
16
|
+
it(
|
|
17
|
+
"Should return API app information",
|
|
18
|
+
async () => {
|
|
19
|
+
const response = await agent.get(`/api`);
|
|
20
|
+
// const response = await agent.get(`/api/${CONFIG.APP.VER}`); 框架例子原始的 版本号;
|
|
21
|
+
expect(response.status).toBe(OK);
|
|
22
|
+
expect(Object.keys(response.body.data)).toEqual([
|
|
23
|
+
"NAME",
|
|
24
|
+
"VERSION",
|
|
25
|
+
"VER",
|
|
26
|
+
"DESCRIPTION",
|
|
27
|
+
"AUTHORS",
|
|
28
|
+
"HOST",
|
|
29
|
+
"PORT",
|
|
30
|
+
"ENV",
|
|
31
|
+
"PATH",
|
|
32
|
+
"MERGE",
|
|
33
|
+
]);
|
|
34
|
+
},
|
|
35
|
+
TIMEOUT,
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
it.each`
|
|
39
|
+
query | field | expectedStatus
|
|
40
|
+
${"name"} | ${"NAME"} | ${OK}
|
|
41
|
+
${"version"} | ${"VERSION"} | ${OK}
|
|
42
|
+
${"description"} | ${"DESCRIPTION"} | ${OK}
|
|
43
|
+
${"authors"} | ${"AUTHORS"} | ${OK}
|
|
44
|
+
${"port"} | ${"PORT"} | ${OK}
|
|
45
|
+
${"env"} | ${"ENV"} | ${OK}
|
|
46
|
+
`(
|
|
47
|
+
"Should return CONFIG.APP[$field] value when query.key is `$query`",
|
|
48
|
+
async ({
|
|
49
|
+
query,
|
|
50
|
+
field,
|
|
51
|
+
expectedStatus,
|
|
52
|
+
}: {
|
|
53
|
+
query: string;
|
|
54
|
+
field: keyof typeof CONFIG.APP;
|
|
55
|
+
expectedStatus: string;
|
|
56
|
+
}) => {
|
|
57
|
+
const response = await agent.get(`/api`).query({ key: query });
|
|
58
|
+
|
|
59
|
+
expect(response.body.status).toBe(expectedStatus);
|
|
60
|
+
expect(response.body.data[field]).toBe(CONFIG.APP[field]);
|
|
61
|
+
},
|
|
62
|
+
TIMEOUT,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
it(
|
|
66
|
+
"Should return 400 status Validation Error",
|
|
67
|
+
async () => {
|
|
68
|
+
const invalidQuery = "invalid-field";
|
|
69
|
+
const response = await agent.get(`/api`).query({ key: invalidQuery });
|
|
70
|
+
|
|
71
|
+
expect(response.body.status).toBe(BAD_REQUEST);
|
|
72
|
+
expect(response.body.message).toBe(HttpStatus[BAD_REQUEST]);
|
|
73
|
+
},
|
|
74
|
+
TIMEOUT,
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
});
|
package/config/test.json
ADDED