tldts-tests 7.2.1 → 7.3.1
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/dist/cjs/src/tldts-tests.js +191 -0
- package/dist/cjs/src/tldts-tests.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/es6/src/tldts-tests.js +191 -0
- package/dist/es6/src/tldts-tests.js.map +1 -1
- package/dist/es6/tsconfig.bundle.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/tldts-tests.ts +277 -0
package/src/tldts-tests.ts
CHANGED
|
@@ -370,6 +370,25 @@ export default function test(
|
|
|
370
370
|
expect(tldts.getDomain('s_pf.google.com')).to.equal('google.com'); // middle '_' (reference case)
|
|
371
371
|
});
|
|
372
372
|
|
|
373
|
+
// A label must not BEGIN with a hyphen (RFC 1034 §3.5 / RFC 1035 §2.3.1 LDH;
|
|
374
|
+
// cf. WHATWG "valid domain" / UTS #46 CheckHyphens). validateHostname is on by
|
|
375
|
+
// default, so an invalid host yields a null parse — exercised here at the
|
|
376
|
+
// public-API level (the inline-validation fast path). @see issue #2395.
|
|
377
|
+
it('should reject labels that begin with a hyphen', () => {
|
|
378
|
+
expect(tldts.getHostname('foo.-example.com')).to.equal(null); // interior label
|
|
379
|
+
expect(tldts.getHostname('a.b.-c.com')).to.equal(null); // deeper interior label
|
|
380
|
+
expect(tldts.getHostname('foo.-com')).to.equal(null); // final label
|
|
381
|
+
expect(tldts.getDomain('foo.-example.com')).to.equal(null);
|
|
382
|
+
expect(tldts.getHostname('-example.com')).to.equal(null); // first label (already rejected)
|
|
383
|
+
// Interior hyphens and underscore-leading labels stay valid.
|
|
384
|
+
expect(tldts.getHostname('foo.ex-ample.com')).to.equal(
|
|
385
|
+
'foo.ex-ample.com',
|
|
386
|
+
);
|
|
387
|
+
expect(tldts.getHostname('_dmarc.example.com')).to.equal(
|
|
388
|
+
'_dmarc.example.com',
|
|
389
|
+
);
|
|
390
|
+
});
|
|
391
|
+
|
|
373
392
|
// @see https://github.com/oncletom/tld.js/issues/53
|
|
374
393
|
it('should correctly extract domain from paths including "@" in the path', () => {
|
|
375
394
|
const domain = tldts.getDomain(
|
|
@@ -731,6 +750,264 @@ export default function test(
|
|
|
731
750
|
});
|
|
732
751
|
});
|
|
733
752
|
|
|
753
|
+
// The default (validating) path fuses host validation into the extraction
|
|
754
|
+
// scan: for a "simple" authority (no userinfo / port / brackets / trailing
|
|
755
|
+
// dot / control char) parseImpl skips the separate isValidHostname pass and
|
|
756
|
+
// trusts the verdict accumulated during extraction. These cases pin the
|
|
757
|
+
// user-facing contract of that fusion — exercised end-to-end through the
|
|
758
|
+
// public API — at the structural boundaries (label/total length, hyphen, dot,
|
|
759
|
+
// underscore positions) and across the simple-vs-complex authority split. All
|
|
760
|
+
// expected values were cross-checked against the v7.2.0 pre-fusion baseline
|
|
761
|
+
// (0 divergences), so an accidental behaviour change here is a regression.
|
|
762
|
+
describe('inline hostname validation (extract/validate fusion)', () => {
|
|
763
|
+
const rep = (s: string, n: number): string => {
|
|
764
|
+
let r = '';
|
|
765
|
+
for (let i = 0; i < n; i += 1) {
|
|
766
|
+
r += s;
|
|
767
|
+
}
|
|
768
|
+
return r;
|
|
769
|
+
};
|
|
770
|
+
|
|
771
|
+
describe('label-length boundary (63 valid / 64 invalid)', () => {
|
|
772
|
+
it('accepts a 63-char first label, rejects 64', () => {
|
|
773
|
+
expect(tldts.getHostname('http://' + rep('a', 63) + '.com/p')).to.equal(
|
|
774
|
+
rep('a', 63) + '.com',
|
|
775
|
+
);
|
|
776
|
+
expect(tldts.getHostname('http://' + rep('a', 64) + '.com/p')).to.equal(
|
|
777
|
+
null,
|
|
778
|
+
);
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
it('accepts a 63-char last label, rejects 64', () => {
|
|
782
|
+
expect(tldts.getHostname('http://ok.' + rep('a', 63))).to.equal(
|
|
783
|
+
'ok.' + rep('a', 63),
|
|
784
|
+
);
|
|
785
|
+
expect(tldts.getHostname('http://ok.' + rep('a', 64))).to.equal(null);
|
|
786
|
+
});
|
|
787
|
+
|
|
788
|
+
it('accepts a single 63-char label, rejects 64 (no dots)', () => {
|
|
789
|
+
// Public suffix of a bare single label is the label itself => no domain,
|
|
790
|
+
// but the hostname must still be extracted (or rejected at 64).
|
|
791
|
+
expect(tldts.getHostname('http://' + rep('a', 63) + '/p')).to.equal(
|
|
792
|
+
rep('a', 63),
|
|
793
|
+
);
|
|
794
|
+
expect(tldts.getHostname('http://' + rep('a', 64) + '/p')).to.equal(
|
|
795
|
+
null,
|
|
796
|
+
);
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
it('agrees between URL and bare-hostname forms at the boundary', () => {
|
|
800
|
+
// The bare form takes the urlIsValidHostname reference path; the URL
|
|
801
|
+
// form takes the inline-validation path. Both must converge.
|
|
802
|
+
expect(tldts.getHostname(rep('a', 63) + '.com')).to.equal(
|
|
803
|
+
rep('a', 63) + '.com',
|
|
804
|
+
);
|
|
805
|
+
expect(tldts.getHostname(rep('a', 64) + '.com')).to.equal(null);
|
|
806
|
+
});
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
describe('total-length boundary (255 valid / 256 invalid)', () => {
|
|
810
|
+
// 'aaa.' * 63 + 'aaa' = 255 chars with every label <= 63, so this isolates
|
|
811
|
+
// the total-length guard from the per-label guard. +1 char = 256.
|
|
812
|
+
const h255 = rep('aaa.', 63) + 'aaa';
|
|
813
|
+
const h256 = rep('aaa.', 63) + 'aaaa';
|
|
814
|
+
|
|
815
|
+
it('precondition: lengths are exactly 255 and 256', () => {
|
|
816
|
+
expect(h255.length).to.equal(255);
|
|
817
|
+
expect(h256.length).to.equal(256);
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
it('accepts a 255-char host, rejects 256 (URL form)', () => {
|
|
821
|
+
expect(tldts.getHostname('http://' + h255 + '/p')).to.equal(h255);
|
|
822
|
+
expect(tldts.getHostname('http://' + h256 + '/p')).to.equal(null);
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
it('accepts a 255-char host, rejects 256 (bare form)', () => {
|
|
826
|
+
expect(tldts.getHostname(h255)).to.equal(h255);
|
|
827
|
+
expect(tldts.getHostname(h256)).to.equal(null);
|
|
828
|
+
});
|
|
829
|
+
});
|
|
830
|
+
|
|
831
|
+
describe('dot placement', () => {
|
|
832
|
+
it('accepts a single leading dot', () => {
|
|
833
|
+
expect(tldts.getHostname('http://.example.com/p')).to.equal(
|
|
834
|
+
'.example.com',
|
|
835
|
+
);
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
it('strips a single and multiple trailing dots', () => {
|
|
839
|
+
expect(tldts.getHostname('http://example.com./p')).to.equal(
|
|
840
|
+
'example.com',
|
|
841
|
+
);
|
|
842
|
+
expect(tldts.getHostname('http://example.com.../p')).to.equal(
|
|
843
|
+
'example.com',
|
|
844
|
+
);
|
|
845
|
+
});
|
|
846
|
+
|
|
847
|
+
it('rejects consecutive dots (empty label)', () => {
|
|
848
|
+
expect(tldts.getHostname('http://a..b.com/p')).to.equal(null);
|
|
849
|
+
expect(tldts.getHostname('a..b.com')).to.equal(null);
|
|
850
|
+
});
|
|
851
|
+
});
|
|
852
|
+
|
|
853
|
+
describe('hyphen placement', () => {
|
|
854
|
+
it('rejects a leading hyphen on the host', () => {
|
|
855
|
+
expect(tldts.getHostname('http://-example.com/p')).to.equal(null);
|
|
856
|
+
expect(tldts.getHostname('-example.com')).to.equal(null);
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
it('rejects a trailing hyphen on the whole host', () => {
|
|
860
|
+
expect(tldts.getHostname('http://example.com-/p')).to.equal(null);
|
|
861
|
+
expect(tldts.getHostname('example.com-')).to.equal(null);
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
it('rejects a label that ends with a hyphen', () => {
|
|
865
|
+
expect(tldts.getHostname('http://foo-.bar.com/p')).to.equal(null);
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
it('accepts a hyphen mid-label', () => {
|
|
869
|
+
expect(tldts.getHostname('http://ex-ample.com/p')).to.equal(
|
|
870
|
+
'ex-ample.com',
|
|
871
|
+
);
|
|
872
|
+
expect(tldts.getHostname('ex-ample.com')).to.equal('ex-ample.com');
|
|
873
|
+
});
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
describe('underscore placement (DNS-lenient, RFC 2181 §11)', () => {
|
|
877
|
+
it('accepts a leading underscore', () => {
|
|
878
|
+
expect(tldts.getHostname('http://_example.com/p')).to.equal(
|
|
879
|
+
'_example.com',
|
|
880
|
+
);
|
|
881
|
+
});
|
|
882
|
+
|
|
883
|
+
it('accepts a mid-label underscore', () => {
|
|
884
|
+
expect(tldts.getHostname('http://ex_ample.com/p')).to.equal(
|
|
885
|
+
'ex_ample.com',
|
|
886
|
+
);
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
it('accepts a label-final underscore', () => {
|
|
890
|
+
expect(tldts.getHostname('http://spf_.example.com/p')).to.equal(
|
|
891
|
+
'spf_.example.com',
|
|
892
|
+
);
|
|
893
|
+
});
|
|
894
|
+
});
|
|
895
|
+
|
|
896
|
+
describe('character classes', () => {
|
|
897
|
+
it('lower-cases an uppercase host then validates it (URL and bare)', () => {
|
|
898
|
+
expect(tldts.getHostname('HTTP://SUB.EXAMPLE.COM/P')).to.equal(
|
|
899
|
+
'sub.example.com',
|
|
900
|
+
);
|
|
901
|
+
expect(tldts.getHostname('EXAMPLE.COM')).to.equal('example.com');
|
|
902
|
+
});
|
|
903
|
+
|
|
904
|
+
it('keeps a non-ASCII / IDN host without punycode (URL and bare)', () => {
|
|
905
|
+
expect(tldts.getHostname('http://bücher.example/p')).to.equal(
|
|
906
|
+
'bücher.example',
|
|
907
|
+
);
|
|
908
|
+
expect(tldts.getHostname('ящик-с-апельсинами.рф')).to.equal(
|
|
909
|
+
'ящик-с-апельсинами.рф',
|
|
910
|
+
);
|
|
911
|
+
});
|
|
912
|
+
|
|
913
|
+
it('accepts a digits-only host (no IPv4 normalisation)', () => {
|
|
914
|
+
expect(tldts.getHostname('http://123.456/p')).to.equal('123.456');
|
|
915
|
+
expect(tldts.getHostname('123.456')).to.equal('123.456');
|
|
916
|
+
});
|
|
917
|
+
|
|
918
|
+
it('rejects a forbidden host character (%, space)', () => {
|
|
919
|
+
expect(tldts.getHostname('http://a%b.com/p')).to.equal(null);
|
|
920
|
+
expect(tldts.getHostname('http://a b.com/p')).to.equal(null);
|
|
921
|
+
});
|
|
922
|
+
});
|
|
923
|
+
|
|
924
|
+
describe('simple-vs-complex authority split (the fusion gate)', () => {
|
|
925
|
+
// The fusion is ACTIVE only for a simple authority; a complex one
|
|
926
|
+
// (userinfo / port / brackets / trailing dot) falls back to the separate
|
|
927
|
+
// isValidHostname pass. Both branches must yield the identical host.
|
|
928
|
+
it('extracts the same host with and without userinfo', () => {
|
|
929
|
+
expect(tldts.getHostname('http://host.com/p')).to.equal('host.com');
|
|
930
|
+
expect(tldts.getHostname('http://user@host.com/p')).to.equal(
|
|
931
|
+
'host.com',
|
|
932
|
+
);
|
|
933
|
+
expect(tldts.getHostname('http://user:pass@host.com/p')).to.equal(
|
|
934
|
+
'host.com',
|
|
935
|
+
);
|
|
936
|
+
});
|
|
937
|
+
|
|
938
|
+
it('extracts the same host with and without a port', () => {
|
|
939
|
+
expect(tldts.getHostname('http://host.com:8080/p')).to.equal(
|
|
940
|
+
'host.com',
|
|
941
|
+
);
|
|
942
|
+
});
|
|
943
|
+
|
|
944
|
+
it('extracts the same host with a trailing-dot (complex) authority', () => {
|
|
945
|
+
expect(tldts.getHostname('http://host.com./p')).to.equal('host.com');
|
|
946
|
+
});
|
|
947
|
+
|
|
948
|
+
it('extracts a bracketed IPv6 host (complex authority)', () => {
|
|
949
|
+
expect(tldts.getHostname('http://[::1]/p')).to.equal('::1');
|
|
950
|
+
});
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
describe('full parse() result parity at the boundary', () => {
|
|
954
|
+
// The fused verdict gates parseImpl BEFORE public-suffix/domain
|
|
955
|
+
// extraction, so a valid simple host must still produce a full result and
|
|
956
|
+
// an invalid one must zero every field.
|
|
957
|
+
it('produces a full result for a valid simple host', () => {
|
|
958
|
+
expect(tldts.parse('http://sub.example.com/p')).to.deep.include({
|
|
959
|
+
hostname: 'sub.example.com',
|
|
960
|
+
domain: 'example.com',
|
|
961
|
+
publicSuffix: 'com',
|
|
962
|
+
});
|
|
963
|
+
});
|
|
964
|
+
|
|
965
|
+
it('zeroes domain and publicSuffix for an invalid simple host', () => {
|
|
966
|
+
expect(tldts.parse('http://-example.com/p')).to.deep.include({
|
|
967
|
+
hostname: null,
|
|
968
|
+
domain: null,
|
|
969
|
+
publicSuffix: null,
|
|
970
|
+
});
|
|
971
|
+
});
|
|
972
|
+
});
|
|
973
|
+
|
|
974
|
+
describe('fusion-inactive controls (must match the validating path)', () => {
|
|
975
|
+
it('validateHostname:false keeps a host the fused path would reject', () => {
|
|
976
|
+
// Fusion never runs; the raw host is returned verbatim.
|
|
977
|
+
expect(
|
|
978
|
+
tldts.parse('http://-example.com/p', { validateHostname: false })
|
|
979
|
+
.hostname,
|
|
980
|
+
).to.equal('-example.com');
|
|
981
|
+
expect(
|
|
982
|
+
tldts.parse('http://' + rep('a', 64) + '.com/p', {
|
|
983
|
+
validateHostname: false,
|
|
984
|
+
}).hostname,
|
|
985
|
+
).to.equal(rep('a', 64) + '.com');
|
|
986
|
+
});
|
|
987
|
+
|
|
988
|
+
it('mixedInputs:false (URL-only path) matches the default for a valid host', () => {
|
|
989
|
+
const url = 'http://sub.example.com/p';
|
|
990
|
+
expect(tldts.parse(url, { mixedInputs: false })).to.deep.equal(
|
|
991
|
+
tldts.parse(url),
|
|
992
|
+
);
|
|
993
|
+
});
|
|
994
|
+
|
|
995
|
+
it('mixedInputs:false rejects an invalid simple host like the default', () => {
|
|
996
|
+
const url = 'http://-example.com/p';
|
|
997
|
+
expect(tldts.parse(url, { mixedInputs: false }).hostname).to.equal(
|
|
998
|
+
null,
|
|
999
|
+
);
|
|
1000
|
+
});
|
|
1001
|
+
|
|
1002
|
+
it('a bare valid hostname (reference path) parses identically to its URL form', () => {
|
|
1003
|
+
expect(tldts.parse('sub.example.com').hostname).to.equal(
|
|
1004
|
+
'sub.example.com',
|
|
1005
|
+
);
|
|
1006
|
+
expect(tldts.parse('sub.example.com').domain).to.equal('example.com');
|
|
1007
|
+
});
|
|
1008
|
+
});
|
|
1009
|
+
});
|
|
1010
|
+
|
|
734
1011
|
describe('getDomainWithoutSuffix method', () => {
|
|
735
1012
|
it('should return null if the domain cannot be found', () => {
|
|
736
1013
|
expect(tldts.getDomainWithoutSuffix('not-a-validHost')).to.equal(null);
|