htmlcmp 1.1.0__tar.gz → 1.1.1__tar.gz
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.
- {htmlcmp-1.1.0 → htmlcmp-1.1.1}/PKG-INFO +13 -1
- {htmlcmp-1.1.0 → htmlcmp-1.1.1}/README.md +12 -0
- {htmlcmp-1.1.0 → htmlcmp-1.1.1}/pyproject.toml +1 -1
- {htmlcmp-1.1.0 → htmlcmp-1.1.1}/src/htmlcmp/compare_output_server.py +145 -46
- {htmlcmp-1.1.0 → htmlcmp-1.1.1}/src/htmlcmp.egg-info/PKG-INFO +13 -1
- {htmlcmp-1.1.0 → htmlcmp-1.1.1}/setup.cfg +0 -0
- {htmlcmp-1.1.0 → htmlcmp-1.1.1}/src/htmlcmp/__init__.py +0 -0
- {htmlcmp-1.1.0 → htmlcmp-1.1.1}/src/htmlcmp/common.py +0 -0
- {htmlcmp-1.1.0 → htmlcmp-1.1.1}/src/htmlcmp/compare_output.py +0 -0
- {htmlcmp-1.1.0 → htmlcmp-1.1.1}/src/htmlcmp/html_render_diff.py +0 -0
- {htmlcmp-1.1.0 → htmlcmp-1.1.1}/src/htmlcmp/tidy_output.py +0 -0
- {htmlcmp-1.1.0 → htmlcmp-1.1.1}/src/htmlcmp.egg-info/SOURCES.txt +0 -0
- {htmlcmp-1.1.0 → htmlcmp-1.1.1}/src/htmlcmp.egg-info/dependency_links.txt +0 -0
- {htmlcmp-1.1.0 → htmlcmp-1.1.1}/src/htmlcmp.egg-info/entry_points.txt +0 -0
- {htmlcmp-1.1.0 → htmlcmp-1.1.1}/src/htmlcmp.egg-info/requires.txt +0 -0
- {htmlcmp-1.1.0 → htmlcmp-1.1.1}/src/htmlcmp.egg-info/top_level.txt +0 -0
- {htmlcmp-1.1.0 → htmlcmp-1.1.1}/tests/test_html_render_diff.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: htmlcmp
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.1
|
|
4
4
|
Summary: Compare HTML files by rendered output
|
|
5
5
|
Author: Andreas Stefl
|
|
6
6
|
Maintainer-email: Andreas Stefl <stefl.andreas@gmail.com>
|
|
@@ -53,3 +53,15 @@ docker run -ti \
|
|
|
53
53
|
```bash
|
|
54
54
|
docker build --tag odr_core_test test/docker
|
|
55
55
|
```
|
|
56
|
+
|
|
57
|
+
## Run locally
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
PYTHONPATH=$(pwd)/src:$PYTHONPATH python ./src/htmlcmp/compare_output_server.py \
|
|
61
|
+
/path/to/REFERENCE \
|
|
62
|
+
/path/to/MONITORED \
|
|
63
|
+
--compare \
|
|
64
|
+
--driver firefox \
|
|
65
|
+
--port 8000 \
|
|
66
|
+
-vv
|
|
67
|
+
```
|
|
@@ -35,3 +35,15 @@ docker run -ti \
|
|
|
35
35
|
```bash
|
|
36
36
|
docker build --tag odr_core_test test/docker
|
|
37
37
|
```
|
|
38
|
+
|
|
39
|
+
## Run locally
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
PYTHONPATH=$(pwd)/src:$PYTHONPATH python ./src/htmlcmp/compare_output_server.py \
|
|
43
|
+
/path/to/REFERENCE \
|
|
44
|
+
/path/to/MONITORED \
|
|
45
|
+
--compare \
|
|
46
|
+
--driver firefox \
|
|
47
|
+
--port 8000 \
|
|
48
|
+
-vv
|
|
49
|
+
```
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import io
|
|
5
5
|
import sys
|
|
6
|
+
import shutil
|
|
6
7
|
import argparse
|
|
7
8
|
import logging
|
|
8
9
|
import threading
|
|
@@ -29,9 +30,10 @@ class Config:
|
|
|
29
30
|
comparator = None
|
|
30
31
|
browser = None
|
|
31
32
|
thread_local = threading.local()
|
|
33
|
+
log_file: Path = None
|
|
32
34
|
|
|
33
35
|
|
|
34
|
-
def result_symbol(result: str):
|
|
36
|
+
def result_symbol(result: str) -> str | None:
|
|
35
37
|
if not isinstance(result, str):
|
|
36
38
|
raise TypeError("Result must be of type str")
|
|
37
39
|
|
|
@@ -41,10 +43,10 @@ def result_symbol(result: str):
|
|
|
41
43
|
return "✔"
|
|
42
44
|
if result == "different":
|
|
43
45
|
return "❌"
|
|
44
|
-
return
|
|
46
|
+
return None
|
|
45
47
|
|
|
46
48
|
|
|
47
|
-
def result_css(result: str):
|
|
49
|
+
def result_css(result: str) -> str | None:
|
|
48
50
|
if not isinstance(result, str):
|
|
49
51
|
raise TypeError("Result must be of type str")
|
|
50
52
|
|
|
@@ -53,8 +55,8 @@ def result_css(result: str):
|
|
|
53
55
|
if result == "same":
|
|
54
56
|
return "color:green;"
|
|
55
57
|
if result == "different":
|
|
56
|
-
return "color:
|
|
57
|
-
return
|
|
58
|
+
return "color:red;"
|
|
59
|
+
return None
|
|
58
60
|
|
|
59
61
|
|
|
60
62
|
class Observer:
|
|
@@ -86,7 +88,7 @@ class Observer:
|
|
|
86
88
|
self._observer.schedule(Handler(Config.path_a), Config.path_a, recursive=True)
|
|
87
89
|
self._observer.schedule(Handler(Config.path_b), Config.path_b, recursive=True)
|
|
88
90
|
|
|
89
|
-
def start(self):
|
|
91
|
+
def start(self) -> None:
|
|
90
92
|
logger.info("Starting watchdog observer")
|
|
91
93
|
self._observer.start()
|
|
92
94
|
|
|
@@ -118,11 +120,11 @@ class Observer:
|
|
|
118
120
|
init_compare(Config.path_a, Config.path_b)
|
|
119
121
|
logger.info("Initial comparison submitted")
|
|
120
122
|
|
|
121
|
-
def stop(self):
|
|
123
|
+
def stop(self) -> None:
|
|
122
124
|
logger.info("Stopping watchdog observer")
|
|
123
125
|
self._observer.stop()
|
|
124
126
|
|
|
125
|
-
def join(self):
|
|
127
|
+
def join(self) -> None:
|
|
126
128
|
logger.info("Joining watchdog observer")
|
|
127
129
|
self._observer.join()
|
|
128
130
|
|
|
@@ -130,10 +132,11 @@ class Observer:
|
|
|
130
132
|
class Comparator:
|
|
131
133
|
def __init__(self, max_workers: int):
|
|
132
134
|
def initializer():
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
browser
|
|
136
|
-
|
|
135
|
+
if Config.driver is not None:
|
|
136
|
+
browser = getattr(Config.thread_local, "browser", None)
|
|
137
|
+
if browser is None:
|
|
138
|
+
browser = get_browser(driver=Config.driver)
|
|
139
|
+
Config.thread_local.browser = browser
|
|
137
140
|
|
|
138
141
|
logger.info(f"Creating comparator with {max_workers} workers")
|
|
139
142
|
|
|
@@ -143,12 +146,16 @@ class Comparator:
|
|
|
143
146
|
self._result = {}
|
|
144
147
|
self._future = {}
|
|
145
148
|
|
|
146
|
-
def submit(self, path: Path):
|
|
149
|
+
def submit(self, path: Path) -> None:
|
|
147
150
|
logger.debug(f"Submitting comparison for path: {path}")
|
|
148
151
|
|
|
149
152
|
if not isinstance(path, Path):
|
|
150
153
|
raise TypeError("Path must be of type Path")
|
|
151
154
|
|
|
155
|
+
if path.suffix.lower() in [".html", ".htm"] and Config.driver is None:
|
|
156
|
+
logger.debug(f"Skipping submission of HTML file without browser: {path}")
|
|
157
|
+
return
|
|
158
|
+
|
|
152
159
|
if path in self._future:
|
|
153
160
|
try:
|
|
154
161
|
self._future[path].cancel()
|
|
@@ -160,7 +167,7 @@ class Comparator:
|
|
|
160
167
|
self._result[path] = "pending"
|
|
161
168
|
self._future[path] = self._executor.submit(self.compare, path)
|
|
162
169
|
|
|
163
|
-
def compare(self, path: Path):
|
|
170
|
+
def compare(self, path: Path) -> None:
|
|
164
171
|
logger.debug(f"Comparing files for path: {path}")
|
|
165
172
|
|
|
166
173
|
if not isinstance(path, Path):
|
|
@@ -177,7 +184,7 @@ class Comparator:
|
|
|
177
184
|
self._result[path] = "same" if result else "different"
|
|
178
185
|
self._future.pop(path)
|
|
179
186
|
|
|
180
|
-
def result(self, path: Path):
|
|
187
|
+
def result(self, path: Path) -> str | None:
|
|
181
188
|
logger.debug(f"Getting comparison result for path: {path}")
|
|
182
189
|
|
|
183
190
|
if not isinstance(path, Path):
|
|
@@ -220,9 +227,13 @@ class Comparator:
|
|
|
220
227
|
|
|
221
228
|
return functools.reduce(
|
|
222
229
|
lambda a, b: (
|
|
223
|
-
|
|
224
|
-
if
|
|
225
|
-
else (
|
|
230
|
+
None
|
|
231
|
+
if None in (a, b)
|
|
232
|
+
else (
|
|
233
|
+
"pending"
|
|
234
|
+
if "pending" in (a, b)
|
|
235
|
+
else "different" if "different" in (a, b) else "same"
|
|
236
|
+
)
|
|
226
237
|
),
|
|
227
238
|
[self.result(path / name) for name in common]
|
|
228
239
|
+ [
|
|
@@ -235,8 +246,8 @@ class Comparator:
|
|
|
235
246
|
"same",
|
|
236
247
|
)
|
|
237
248
|
|
|
238
|
-
logger.
|
|
239
|
-
return
|
|
249
|
+
logger.debug(f"No comparison result for path: {path}")
|
|
250
|
+
return None
|
|
240
251
|
|
|
241
252
|
|
|
242
253
|
app = Flask("compare")
|
|
@@ -248,7 +259,7 @@ def root():
|
|
|
248
259
|
|
|
249
260
|
current_entry_id = 0
|
|
250
261
|
|
|
251
|
-
def next_entry_id():
|
|
262
|
+
def next_entry_id() -> int:
|
|
252
263
|
nonlocal current_entry_id
|
|
253
264
|
entry_id = current_entry_id
|
|
254
265
|
current_entry_id += 1
|
|
@@ -284,7 +295,7 @@ def root():
|
|
|
284
295
|
result += "<td></td>"
|
|
285
296
|
|
|
286
297
|
if is_comparable:
|
|
287
|
-
main = f'<a href="/compare/{path}">{name}</a>'
|
|
298
|
+
main = f'<a href="/compare/{path}" target="_blank">{name}</a>'
|
|
288
299
|
else:
|
|
289
300
|
main = f"{name}"
|
|
290
301
|
if cmp_result is not None:
|
|
@@ -297,6 +308,10 @@ def root():
|
|
|
297
308
|
result += f'<td class="main" style="{style}">{main}</td>'
|
|
298
309
|
result += f"<td>{message}</td>"
|
|
299
310
|
|
|
311
|
+
result += (
|
|
312
|
+
f"<td><button onclick=\"updateRef('{path}')\">update ref</button></td>"
|
|
313
|
+
)
|
|
314
|
+
|
|
300
315
|
result += "</tr>"
|
|
301
316
|
|
|
302
317
|
return result
|
|
@@ -435,10 +450,17 @@ tr {
|
|
|
435
450
|
|
|
436
451
|
result += "<p>"
|
|
437
452
|
result += "comparing<br>"
|
|
438
|
-
result += f"
|
|
439
|
-
result += f"
|
|
453
|
+
result += f"Reference: {Config.path_a}<br>"
|
|
454
|
+
result += f"Monitored: {Config.path_b}"
|
|
440
455
|
result += "</p>"
|
|
441
456
|
|
|
457
|
+
if Config.log_file is not None:
|
|
458
|
+
result += "<p>"
|
|
459
|
+
result += (
|
|
460
|
+
f'<a href="/logfile" target="_blank">View log file: {Config.log_file}</a>'
|
|
461
|
+
)
|
|
462
|
+
result += "</p>"
|
|
463
|
+
|
|
442
464
|
result += "<p>"
|
|
443
465
|
result += '<button onclick="toggleAll(true)">Expand All</button>'
|
|
444
466
|
result += '<button onclick="toggleAll(false)">Collapse All</button>'
|
|
@@ -446,7 +468,7 @@ tr {
|
|
|
446
468
|
|
|
447
469
|
result += "<table>"
|
|
448
470
|
result += "<thead>"
|
|
449
|
-
result += "<tr><td></td><td></td><td>Name</td><td>Message</td></tr>"
|
|
471
|
+
result += "<tr><td></td><td></td><td>Name</td><td>Message</td><td>Actions</td></tr>"
|
|
450
472
|
result += "</thead>"
|
|
451
473
|
result += "<tbody>"
|
|
452
474
|
result += generate_tree(Config.path_a, Config.path_b, "", None, 0)
|
|
@@ -485,6 +507,21 @@ function toggleAll(show) {
|
|
|
485
507
|
}
|
|
486
508
|
});
|
|
487
509
|
}
|
|
510
|
+
|
|
511
|
+
function updateRef(path) {
|
|
512
|
+
fetch(`/update_ref/${path}`)
|
|
513
|
+
.then(response => {
|
|
514
|
+
if (response.ok) {
|
|
515
|
+
alert(`Reference updated for ${path}`);
|
|
516
|
+
location.reload();
|
|
517
|
+
} else {
|
|
518
|
+
alert(`Failed to update reference for ${path}: ${response.statusText}`);
|
|
519
|
+
}
|
|
520
|
+
})
|
|
521
|
+
.catch(error => {
|
|
522
|
+
alert(`Error updating reference for ${path}: ${error}`);
|
|
523
|
+
});
|
|
524
|
+
}
|
|
488
525
|
</script>
|
|
489
526
|
</body>
|
|
490
527
|
</html>
|
|
@@ -493,6 +530,16 @@ function toggleAll(show) {
|
|
|
493
530
|
return result
|
|
494
531
|
|
|
495
532
|
|
|
533
|
+
@app.route("/logfile")
|
|
534
|
+
def logfile():
|
|
535
|
+
logger.debug("Serving log file")
|
|
536
|
+
|
|
537
|
+
if Config.log_file is None:
|
|
538
|
+
return "No log file configured", 404
|
|
539
|
+
|
|
540
|
+
return send_from_directory(Config.log_file.parent, Config.log_file.name)
|
|
541
|
+
|
|
542
|
+
|
|
496
543
|
@app.route("/compare/<path:path>")
|
|
497
544
|
def compare(path: str):
|
|
498
545
|
logger.debug(f"Generating comparison page for path: {path}")
|
|
@@ -509,15 +556,15 @@ html,body {{height:100%;margin:0;}}
|
|
|
509
556
|
</head>
|
|
510
557
|
<body style="display:flex;flex-flow:row;">
|
|
511
558
|
<div style="display:flex;flex:1;flex-flow:column;margin:5px;">
|
|
512
|
-
<a href="/file/a/{path}">{Config.path_a / path}</a>
|
|
559
|
+
<a href="/file/a/{path}" target="_blank">{Config.path_a / path}</a>
|
|
513
560
|
<iframe id="a" src="/file/a/{path}" title="a" frameborder="0" align="left" style="flex:1;"></iframe>
|
|
514
561
|
</div>
|
|
515
562
|
<div style="display:flex;flex:0 0 50px;flex-flow:column;">
|
|
516
|
-
<a href="/image_diff/{path}">diff</a>
|
|
563
|
+
<a href="/image_diff/{path}" target="_blank">diff</a>
|
|
517
564
|
<img src="/image_diff/{path}" width="50" height="0" style="flex:1;">
|
|
518
565
|
</div>
|
|
519
566
|
<div style="display:flex;flex:1;flex-flow:column;margin:5px;">
|
|
520
|
-
<a href="/file/b/{path}">{Config.path_b / path}</a>
|
|
567
|
+
<a href="/file/b/{path}" target="_blank">{Config.path_b / path}</a>
|
|
521
568
|
<iframe id="b" src="/file/b/{path}" title="b" frameborder="0" align="right" style="flex:1;"></iframe>
|
|
522
569
|
</div>
|
|
523
570
|
<script>
|
|
@@ -542,6 +589,9 @@ def image_diff(path: str):
|
|
|
542
589
|
if not isinstance(path, str):
|
|
543
590
|
raise TypeError("Path must be a string")
|
|
544
591
|
|
|
592
|
+
if Config.driver is None:
|
|
593
|
+
return "Image diff not available without browser driver", 404
|
|
594
|
+
|
|
545
595
|
diff, _ = html_render_diff(
|
|
546
596
|
Config.path_a / path,
|
|
547
597
|
Config.path_b / path,
|
|
@@ -566,15 +616,25 @@ def file(variant: str, path: str):
|
|
|
566
616
|
return send_from_directory(variant_root, path)
|
|
567
617
|
|
|
568
618
|
|
|
569
|
-
def
|
|
619
|
+
def verbosity_to_level(verbosity: int) -> int:
|
|
570
620
|
if verbosity >= 3:
|
|
571
|
-
|
|
621
|
+
return logging.DEBUG
|
|
572
622
|
elif verbosity == 2:
|
|
573
|
-
|
|
623
|
+
return logging.INFO
|
|
574
624
|
elif verbosity == 1:
|
|
575
|
-
|
|
625
|
+
return logging.WARNING
|
|
576
626
|
else:
|
|
577
|
-
|
|
627
|
+
return logging.ERROR
|
|
628
|
+
|
|
629
|
+
|
|
630
|
+
def setup_logging(
|
|
631
|
+
verbosity: int, log_file: Path = None, log_file_verbosity: int = None
|
|
632
|
+
) -> None:
|
|
633
|
+
level = verbosity_to_level(verbosity)
|
|
634
|
+
|
|
635
|
+
root_logger = logging.getLogger()
|
|
636
|
+
root_logger.setLevel(level)
|
|
637
|
+
root_logger.handlers.clear()
|
|
578
638
|
|
|
579
639
|
formatter = logging.Formatter(
|
|
580
640
|
fmt="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
|
@@ -582,21 +642,49 @@ def setup_logging(verbosity: int):
|
|
|
582
642
|
)
|
|
583
643
|
|
|
584
644
|
console_handler = logging.StreamHandler(sys.stderr)
|
|
645
|
+
console_handler.setLevel(level)
|
|
585
646
|
console_handler.setFormatter(formatter)
|
|
586
|
-
|
|
587
|
-
root_logger = logging.getLogger()
|
|
588
|
-
root_logger.setLevel(level)
|
|
589
|
-
root_logger.handlers.clear()
|
|
590
647
|
root_logger.addHandler(console_handler)
|
|
591
648
|
|
|
649
|
+
if log_file is not None:
|
|
650
|
+
file_level = (
|
|
651
|
+
verbosity_to_level(log_file_verbosity)
|
|
652
|
+
if log_file_verbosity is not None
|
|
653
|
+
else level
|
|
654
|
+
)
|
|
655
|
+
|
|
656
|
+
file_handler = logging.FileHandler(log_file, mode="a", encoding="utf-8")
|
|
657
|
+
file_handler.setLevel(file_level)
|
|
658
|
+
file_handler.setFormatter(formatter)
|
|
659
|
+
root_logger.addHandler(file_handler)
|
|
660
|
+
|
|
661
|
+
|
|
662
|
+
@app.route("/update_ref/<path:path>")
|
|
663
|
+
def update_ref(path: str):
|
|
664
|
+
logger.debug(f"Updating reference for path: {path}")
|
|
665
|
+
|
|
666
|
+
if not isinstance(path, str):
|
|
667
|
+
raise TypeError("Path must be a string")
|
|
668
|
+
|
|
669
|
+
src = Config.path_b / path
|
|
670
|
+
dst = Config.path_a / path
|
|
671
|
+
|
|
672
|
+
if not src.exists():
|
|
673
|
+
return f"Source file does not exist: {src}", 404
|
|
674
|
+
|
|
675
|
+
if src.is_file():
|
|
676
|
+
shutil.copy2(src, dst)
|
|
677
|
+
else:
|
|
678
|
+
shutil.copytree(src, dst, dirs_exist_ok=True)
|
|
679
|
+
|
|
680
|
+
return "Reference updated", 200
|
|
681
|
+
|
|
592
682
|
|
|
593
683
|
def main():
|
|
594
684
|
parser = argparse.ArgumentParser()
|
|
595
|
-
parser.add_argument("
|
|
596
|
-
parser.add_argument("
|
|
597
|
-
parser.add_argument(
|
|
598
|
-
"--driver", choices=["chrome", "firefox", "phantomjs"], default="firefox"
|
|
599
|
-
)
|
|
685
|
+
parser.add_argument("ref", type=Path, help="Path to the reference directory")
|
|
686
|
+
parser.add_argument("mon", type=Path, help="Path to the monitored directory")
|
|
687
|
+
parser.add_argument("--driver", choices=["chrome", "firefox", "phantomjs"])
|
|
600
688
|
parser.add_argument("--max-workers", type=int, default=1)
|
|
601
689
|
parser.add_argument("--compare", action="store_true")
|
|
602
690
|
parser.add_argument("--port", type=int, default=5000)
|
|
@@ -607,14 +695,25 @@ def main():
|
|
|
607
695
|
default=0,
|
|
608
696
|
help="Increase verbosity (-v, -vv, -vvv)",
|
|
609
697
|
)
|
|
698
|
+
parser.add_argument(
|
|
699
|
+
"--log-file",
|
|
700
|
+
type=Path,
|
|
701
|
+
help="Path to log file",
|
|
702
|
+
)
|
|
703
|
+
parser.add_argument(
|
|
704
|
+
"--log-file-verbosity", type=int, help="Log file verbosity level"
|
|
705
|
+
)
|
|
610
706
|
args = parser.parse_args()
|
|
611
707
|
|
|
612
|
-
setup_logging(args.verbose)
|
|
708
|
+
setup_logging(args.verbose, args.log_file, args.log_file_verbosity)
|
|
613
709
|
|
|
614
|
-
Config.path_a = args.
|
|
615
|
-
Config.path_b = args.
|
|
710
|
+
Config.path_a = args.ref
|
|
711
|
+
Config.path_b = args.mon
|
|
616
712
|
Config.driver = args.driver
|
|
617
|
-
Config.browser =
|
|
713
|
+
Config.browser = (
|
|
714
|
+
get_browser(driver=args.driver) if args.driver is not None else None
|
|
715
|
+
)
|
|
716
|
+
Config.log_file = args.log_file
|
|
618
717
|
|
|
619
718
|
if args.compare:
|
|
620
719
|
Config.comparator = Comparator(max_workers=args.max_workers)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: htmlcmp
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.1
|
|
4
4
|
Summary: Compare HTML files by rendered output
|
|
5
5
|
Author: Andreas Stefl
|
|
6
6
|
Maintainer-email: Andreas Stefl <stefl.andreas@gmail.com>
|
|
@@ -53,3 +53,15 @@ docker run -ti \
|
|
|
53
53
|
```bash
|
|
54
54
|
docker build --tag odr_core_test test/docker
|
|
55
55
|
```
|
|
56
|
+
|
|
57
|
+
## Run locally
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
PYTHONPATH=$(pwd)/src:$PYTHONPATH python ./src/htmlcmp/compare_output_server.py \
|
|
61
|
+
/path/to/REFERENCE \
|
|
62
|
+
/path/to/MONITORED \
|
|
63
|
+
--compare \
|
|
64
|
+
--driver firefox \
|
|
65
|
+
--port 8000 \
|
|
66
|
+
-vv
|
|
67
|
+
```
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|