• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MyIntervals / PHP-CSS-Parser / 13226472805

09 Feb 2025 02:09PM UTC coverage: 49.156%. Remained the same
13226472805

Pull #885

github

web-flow
Merge 6ff07929c into f7914f8b4
Pull Request #885: [TASK] Only allow `string` for some `OutputFormat` properties

932 of 1896 relevant lines covered (49.16%)

10.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.44
/src/OutputFormat.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Sabberworm\CSS;
6

7
class OutputFormat
8
{
9
    /**
10
     * Value format: `"` means double-quote, `'` means single-quote
11
     *
12
     * @var string
13
     *
14
     * @internal since 8.8.0, will be made private in 9.0.0
15
     */
16
    public $sStringQuotingType = '"';
17

18
    /**
19
     * Output RGB colors in hash notation if possible
20
     *
21
     * @var string
22
     *
23
     * @internal since 8.8.0, will be made private in 9.0.0
24
     */
25
    public $bRGBHashNotation = true;
26

27
    /**
28
     * Declaration format
29
     *
30
     * Semicolon after the last rule of a declaration block can be omitted. To do that, set this false.
31
     *
32
     * @var bool
33
     *
34
     * @internal since 8.8.0, will be made private in 9.0.0
35
     */
36
    public $bSemicolonAfterLastRule = true;
37

38
    /**
39
     * Spacing
40
     * Note that these strings are not sanity-checked: the value should only consist of whitespace
41
     * Any newline character will be indented according to the current level.
42
     * The triples (After, Before, Between) can be set using a wildcard (e.g. `$oFormat->set('Space*Rules', "\n");`)
43
     *
44
     * @var string
45
     *
46
     * @internal since 8.8.0, will be made private in 9.0.0
47
     */
48
    public $sSpaceAfterRuleName = ' ';
49

50
    /**
51
     * @var string
52
     *
53
     * @internal since 8.8.0, will be made private in 9.0.0
54
     */
55
    public $sSpaceBeforeRules = '';
56

57
    /**
58
     * @var string
59
     *
60
     * @internal since 8.8.0, will be made private in 9.0.0
61
     */
62
    public $sSpaceAfterRules = '';
63

64
    /**
65
     * @var string
66
     *
67
     * @internal since 8.8.0, will be made private in 9.0.0
68
     */
69
    public $sSpaceBetweenRules = '';
70

71
    /**
72
     * @var string
73
     *
74
     * @internal since 8.8.0, will be made private in 9.0.0
75
     */
76
    public $sSpaceBeforeBlocks = '';
77

78
    /**
79
     * @var string
80
     *
81
     * @internal since 8.8.0, will be made private in 9.0.0
82
     */
83
    public $sSpaceAfterBlocks = '';
84

85
    /**
86
     * @var string
87
     *
88
     * @internal since 8.8.0, will be made private in 9.0.0
89
     */
90
    public $sSpaceBetweenBlocks = "\n";
91

92
    /**
93
     * Content injected in and around at-rule blocks.
94
     *
95
     * @var string
96
     *
97
     * @internal since 8.8.0, will be made private in 9.0.0
98
     */
99
    public $sBeforeAtRuleBlock = '';
100

101
    /**
102
     * @var string
103
     *
104
     * @internal since 8.8.0, will be made private in 9.0.0
105
     */
106
    public $sAfterAtRuleBlock = '';
107

108
    /**
109
     * This is what’s printed before and after the comma if a declaration block contains multiple selectors.
110
     *
111
     * @var string
112
     *
113
     * @internal since 8.8.0, will be made private in 9.0.0
114
     */
115
    public $sSpaceBeforeSelectorSeparator = '';
116

117
    /**
118
     * @var string
119
     *
120
     * @internal since 8.8.0, will be made private in 9.0.0
121
     */
122
    public $sSpaceAfterSelectorSeparator = ' ';
123

124
    /**
125
     * This is what’s inserted before the separator in value lists, by default.
126
     *
127
* <<<<<<< HEAD
128
     * `array` is deprecated in version 8.8.0, and will be removed in version 9.0.0.
129
     * To set the spacing for specific separators, use {@see $aSpaceBeforeListArgumentSeparators} instead.
130
     *
131
     * @var string
132
     *
133
     * @internal since 8.8.0, will be made private in 9.0.0
134
     */
135
    public $sSpaceBeforeListArgumentSeparator = '';
136

137
    /**
138
     * Keys are separators (e.g. `,`).  Values are the space sequence to insert, or an empty string.
139
     *
140
     * @var array<non-empty-string, string>
141
     *
142
     * @internal since 8.8.0, will be made private in 9.0.0
143
     */
144
    public $aSpaceBeforeListArgumentSeparators = [];
145

146
    /**
147
     * This is what’s inserted after the separator in value lists, by default.
148
     *
149
     * @var string
150
     *
151
     * @internal since 8.8.0, will be made private in 9.0.0
152
     */
153
    public $sSpaceAfterListArgumentSeparator = '';
154

155
    /**
156
     * Keys are separators (e.g. `,`).  Values are the space sequence to insert, or an empty string.
157
     *
158
     * @var array<non-empty-string, string>
159
     *
160
     * @internal since 8.8.0, will be made private in 9.0.0
161
     */
162
    public $aSpaceAfterListArgumentSeparators = [];
163

164
    /**
165
     * @var string
166
     *
167
     * @internal since 8.8.0, will be made private in 9.0.0
168
     */
169
    public $sSpaceBeforeOpeningBrace = ' ';
170

171
    /**
172
     * Content injected in and around declaration blocks.
173
     *
174
     * @var string
175
     *
176
     * @internal since 8.8.0, will be made private in 9.0.0
177
     */
178
    public $sBeforeDeclarationBlock = '';
179

180
    /**
181
     * @var string
182
     *
183
     * @internal since 8.8.0, will be made private in 9.0.0
184
     */
185
    public $sAfterDeclarationBlockSelectors = '';
186

187
    /**
188
     * @var string
189
     *
190
     * @internal since 8.8.0, will be made private in 9.0.0
191
     */
192
    public $sAfterDeclarationBlock = '';
193

194
    /**
195
     * Indentation character(s) per level. Only applicable if newlines are used in any of the spacing settings.
196
     *
197
     * @var string
198
     *
199
     * @internal since 8.8.0, will be made private in 9.0.0
200
     */
201
    public $sIndentation = "\t";
202

203
    /**
204
     * Output exceptions.
205
     *
206
     * @var bool
207
     *
208
     * @internal since 8.8.0, will be made private in 9.0.0
209
     */
210
    public $bIgnoreExceptions = false;
211

212
    /**
213
     * Render comments for lists and RuleSets
214
     *
215
     * @var bool
216
     *
217
     * @internal since 8.8.0, will be made private in 9.0.0
218
     */
219
    public $bRenderComments = false;
220

221
    /**
222
     * @var OutputFormatter|null
223
     */
224
    private $oFormatter = null;
225

226
    /**
227
     * @var OutputFormat|null
228
     */
229
    private $oNextLevelFormat = null;
230

231
    /**
232
     * @var int
233
     */
234
    private $iIndentationLevel = 0;
235

236
    public function __construct() {}
237

238
    /**
239
     * @return string|int|bool|null
240
     */
241
    public function get(string $sName)
20✔
242
    {
243
        $aVarPrefixes = ['a', 's', 'm', 'b', 'f', 'o', 'c', 'i'];
20✔
244
        foreach ($aVarPrefixes as $sPrefix) {
20✔
245
            $sFieldName = $sPrefix . \ucfirst($sName);
20✔
246
            if (isset($this->$sFieldName)) {
20✔
247
                return $this->$sFieldName;
20✔
248
            }
249
        }
250
        return null;
×
251
    }
252

253
    /**
254
     * @param array<array-key, string>|string $aNames
255
     * @param mixed $mValue
256
     *
257
     * @return self|false
258
     */
259
    public function set($aNames, $mValue)
8✔
260
    {
261
        $aVarPrefixes = ['a', 's', 'm', 'b', 'f', 'o', 'c', 'i'];
8✔
262
        if (\is_string($aNames) && \strpos($aNames, '*') !== false) {
8✔
263
            $aNames =
264
                [
265
                    \str_replace('*', 'Before', $aNames),
8✔
266
                    \str_replace('*', 'Between', $aNames),
8✔
267
                    \str_replace('*', 'After', $aNames),
8✔
268
                ];
269
        } elseif (!\is_array($aNames)) {
3✔
270
            $aNames = [$aNames];
3✔
271
        }
272
        foreach ($aVarPrefixes as $sPrefix) {
8✔
273
            $bDidReplace = false;
8✔
274
            foreach ($aNames as $sName) {
8✔
275
                $sFieldName = $sPrefix . \ucfirst($sName);
8✔
276
                if (isset($this->$sFieldName)) {
8✔
277
                    $this->$sFieldName = $mValue;
8✔
278
                    $bDidReplace = true;
8✔
279
                }
280
            }
281
            if ($bDidReplace) {
8✔
282
                return $this;
8✔
283
            }
284
        }
285
        // Break the chain so the user knows this option is invalid
286
        return false;
×
287
    }
288

289
    /**
290
     * @param array<array-key, mixed> $aArguments
291
     *
292
     * @return mixed
293
     *
294
     * @throws \Exception
295
     */
296
    public function __call(string $sMethodName, array $aArguments)
20✔
297
    {
298
        if (\method_exists(OutputFormatter::class, $sMethodName)) {
20✔
299
            return \call_user_func_array([$this->getFormatter(), $sMethodName], $aArguments);
20✔
300
        } else {
301
            throw new \Exception('Unknown OutputFormat method called: ' . $sMethodName);
×
302
        }
303
    }
304

305
    /**
306
     * @internal
307
     */
308
    public function getStringQuotingType(): string
21✔
309
    {
310
        return $this->sStringQuotingType;
21✔
311
    }
312

313
    /**
314
     * @return $this fluent interface
315
     */
316
    public function setStringQuotingType(string $quotingType): self
3✔
317
    {
318
        $this->sStringQuotingType = $quotingType;
3✔
319

320
        return $this;
3✔
321
    }
322

323
    /**
324
     * @internal
325
     */
326
    public function getRGBHashNotation(): bool
23✔
327
    {
328
        return $this->bRGBHashNotation;
23✔
329
    }
330

331
    /**
332
     * @return $this fluent interface
333
     */
334
    public function setRGBHashNotation(bool $rgbHashNotation): self
4✔
335
    {
336
        $this->bRGBHashNotation = $rgbHashNotation;
4✔
337

338
        return $this;
4✔
339
    }
340

341
    /**
342
     * @internal
343
     */
344
    public function getSemicolonAfterLastRule(): bool
3✔
345
    {
346
        return $this->bSemicolonAfterLastRule;
3✔
347
    }
348

349
    /**
350
     * @return $this fluent interface
351
     */
352
    public function setSemicolonAfterLastRule(bool $semicolonAfterLastRule): self
4✔
353
    {
354
        $this->bSemicolonAfterLastRule = $semicolonAfterLastRule;
4✔
355

356
        return $this;
4✔
357
    }
358

359
    /**
360
     * @internal
361
     */
362
    public function getSpaceAfterRuleName(): string
2✔
363
    {
364
        return $this->sSpaceAfterRuleName;
2✔
365
    }
366

367
    /**
368
     * @return $this fluent interface
369
     */
370
    public function setSpaceAfterRuleName(string $whitespace): self
6✔
371
    {
372
        $this->sSpaceAfterRuleName = $whitespace;
6✔
373

374
        return $this;
6✔
375
    }
376

377
    /**
378
     * @internal
379
     */
380
    public function getSpaceBeforeRules(): string
2✔
381
    {
382
        return $this->sSpaceBeforeRules;
2✔
383
    }
384

385
    /**
386
     * @return $this fluent interface
387
     */
388
    public function setSpaceBeforeRules(string $whitespace): self
2✔
389
    {
390
        $this->sSpaceBeforeRules = $whitespace;
2✔
391

392
        return $this;
2✔
393
    }
394

395
    /**
396
     * @internal
397
     */
398
    public function getSpaceAfterRules(): string
2✔
399
    {
400
        return $this->sSpaceAfterRules;
2✔
401
    }
402

403
    /**
404
     * @return $this fluent interface
405
     */
406
    public function setSpaceAfterRules(string $whitespace): self
2✔
407
    {
408
        $this->sSpaceAfterRules = $whitespace;
2✔
409

410
        return $this;
2✔
411
    }
412

413
    /**
414
     * @internal
415
     */
416
    public function getSpaceBetweenRules(): string
2✔
417
    {
418
        return $this->sSpaceBetweenRules;
2✔
419
    }
420

421
    /**
422
     * @return $this fluent interface
423
     */
424
    public function setSpaceBetweenRules(string $whitespace): self
2✔
425
    {
426
        $this->sSpaceBetweenRules = $whitespace;
2✔
427

428
        return $this;
2✔
429
    }
430

431
    /**
432
     * @internal
433
     */
434
    public function getSpaceBeforeBlocks(): string
2✔
435
    {
436
        return $this->sSpaceBeforeBlocks;
2✔
437
    }
438

439
    /**
440
     * @return $this fluent interface
441
     */
442
    public function setSpaceBeforeBlocks(string $whitespace): self
2✔
443
    {
444
        $this->sSpaceBeforeBlocks = $whitespace;
2✔
445

446
        return $this;
2✔
447
    }
448

449
    /**
450
     * @internal
451
     */
452
    public function getSpaceAfterBlocks(): string
2✔
453
    {
454
        return $this->sSpaceAfterBlocks;
2✔
455
    }
456

457
    /**
458
     * @return $this fluent interface
459
     */
460
    public function setSpaceAfterBlocks(string $whitespace): self
2✔
461
    {
462
        $this->sSpaceAfterBlocks = $whitespace;
2✔
463

464
        return $this;
2✔
465
    }
466

467
    /**
468
     * @internal
469
     */
470
    public function getSpaceBetweenBlocks(): string
2✔
471
    {
472
        return $this->sSpaceBetweenBlocks;
2✔
473
    }
474

475
    /**
476
     * @return $this fluent interface
477
     */
478
    public function setSpaceBetweenBlocks(string $whitespace): self
6✔
479
    {
480
        $this->sSpaceBetweenBlocks = $whitespace;
6✔
481

482
        return $this;
6✔
483
    }
484

485
    /**
486
     * @internal
487
     */
488
    public function getBeforeAtRuleBlock(): string
2✔
489
    {
490
        return $this->sBeforeAtRuleBlock;
2✔
491
    }
492

493
    /**
494
     * @return $this fluent interface
495
     */
496
    public function setBeforeAtRuleBlock(string $content): self
2✔
497
    {
498
        $this->sBeforeAtRuleBlock = $content;
2✔
499

500
        return $this;
2✔
501
    }
502

503
    /**
504
     * @internal
505
     */
506
    public function getAfterAtRuleBlock(): string
2✔
507
    {
508
        return $this->sAfterAtRuleBlock;
2✔
509
    }
510

511
    /**
512
     * @return $this fluent interface
513
     */
514
    public function setAfterAtRuleBlock(string $content): self
2✔
515
    {
516
        $this->sAfterAtRuleBlock = $content;
2✔
517

518
        return $this;
2✔
519
    }
520

521
    /**
522
     * @internal
523
     */
524
    public function getSpaceBeforeSelectorSeparator(): string
2✔
525
    {
526
        return $this->sSpaceBeforeSelectorSeparator;
2✔
527
    }
528

529
    /**
530
     * @return $this fluent interface
531
     */
532
    public function setSpaceBeforeSelectorSeparator(string $whitespace): self
2✔
533
    {
534
        $this->sSpaceBeforeSelectorSeparator = $whitespace;
2✔
535

536
        return $this;
2✔
537
    }
538

539
    /**
540
     * @internal
541
     */
542
    public function getSpaceAfterSelectorSeparator(): string
2✔
543
    {
544
        return $this->sSpaceAfterSelectorSeparator;
2✔
545
    }
546

547
    /**
548
     * @return $this fluent interface
549
     */
550
    public function setSpaceAfterSelectorSeparator(string $whitespace): self
6✔
551
    {
552
        $this->sSpaceAfterSelectorSeparator = $whitespace;
6✔
553

554
        return $this;
6✔
555
    }
556

557
    /**
558
     * @internal
559
     */
560
    public function getSpaceBeforeListArgumentSeparator(): string
2✔
561
    {
562
        return $this->sSpaceBeforeListArgumentSeparator;
2✔
563
    }
564

565
    /**
566
     * @return $this fluent interface
567
     */
568
    public function setSpaceBeforeListArgumentSeparator(string $whitespace): self
2✔
569
    {
570
        $this->sSpaceBeforeListArgumentSeparator = $whitespace;
2✔
571

572
        return $this;
2✔
573
    }
574

575
    /**
576
     * @return array<non-empty-string, string>
577
     *
578
     * @internal
579
     */
580
    public function getSpaceBeforeListArgumentSeparators(): array
20✔
581
    {
582
        return $this->aSpaceBeforeListArgumentSeparators;
20✔
583
    }
584

585
    /**
586
     * @param array<non-empty-string, string> $separatorSpaces
587
     *
588
     * @return $this fluent interface
589
     */
590
    public function setSpaceBeforeListArgumentSeparators(array $separatorSpaces): self
2✔
591
    {
592
        $this->aSpaceBeforeListArgumentSeparators = $separatorSpaces;
2✔
593

594
        return $this;
2✔
595
    }
596

597
    /**
598
     * @internal
599
     */
600
    public function getSpaceAfterListArgumentSeparator(): string
2✔
601
    {
602
        return $this->sSpaceAfterListArgumentSeparator;
2✔
603
    }
604

605
    /**
606
     * @return $this fluent interface
607
     */
608
    public function setSpaceAfterListArgumentSeparator(string $whitespace): self
4✔
609
    {
610
        $this->sSpaceAfterListArgumentSeparator = $whitespace;
4✔
611

612
        return $this;
4✔
613
    }
614

615
    /**
616
     * @return array<non-empty-string, string>
617
     *
618
     * @internal
619
     */
620
    public function getSpaceAfterListArgumentSeparators(): array
20✔
621
    {
622
        return $this->aSpaceAfterListArgumentSeparators;
20✔
623
    }
624

625
    /**
626
     * @param array<non-empty-string, string> $separatorSpaces
627
     *
628
     * @return $this fluent interface
629
     */
630
    public function setSpaceAfterListArgumentSeparators(array $separatorSpaces): self
3✔
631
    {
632
        $this->aSpaceAfterListArgumentSeparators = $separatorSpaces;
3✔
633

634
        return $this;
3✔
635
    }
636

637
    /**
638
     * @internal
639
     */
640
    public function getSpaceBeforeOpeningBrace(): string
2✔
641
    {
642
        return $this->sSpaceBeforeOpeningBrace;
2✔
643
    }
644

645
    /**
646
     * @return $this fluent interface
647
     */
648
    public function setSpaceBeforeOpeningBrace(string $whitespace): self
6✔
649
    {
650
        $this->sSpaceBeforeOpeningBrace = $whitespace;
6✔
651

652
        return $this;
6✔
653
    }
654

655
    /**
656
     * @internal
657
     */
658
    public function getBeforeDeclarationBlock(): string
2✔
659
    {
660
        return $this->sBeforeDeclarationBlock;
2✔
661
    }
662

663
    /**
664
     * @return $this fluent interface
665
     */
666
    public function setBeforeDeclarationBlock(string $content): self
2✔
667
    {
668
        $this->sBeforeDeclarationBlock = $content;
2✔
669

670
        return $this;
2✔
671
    }
672

673
    /**
674
     * @internal
675
     */
676
    public function getAfterDeclarationBlockSelectors(): string
2✔
677
    {
678
        return $this->sAfterDeclarationBlockSelectors;
2✔
679
    }
680

681
    /**
682
     * @return $this fluent interface
683
     */
684
    public function setAfterDeclarationBlockSelectors(string $content): self
2✔
685
    {
686
        $this->sAfterDeclarationBlockSelectors = $content;
2✔
687

688
        return $this;
2✔
689
    }
690

691
    /**
692
     * @internal
693
     */
694
    public function getAfterDeclarationBlock(): string
2✔
695
    {
696
        return $this->sAfterDeclarationBlock;
2✔
697
    }
698

699
    /**
700
     * @return $this fluent interface
701
     */
702
    public function setAfterDeclarationBlock(string $content): self
2✔
703
    {
704
        $this->sAfterDeclarationBlock = $content;
2✔
705

706
        return $this;
2✔
707
    }
708

709
    /**
710
     * @internal
711
     */
712
    public function getIndentation(): string
13✔
713
    {
714
        return $this->sIndentation;
13✔
715
    }
716

717
    /**
718
     * @return $this fluent interface
719
     */
720
    public function setIndentation(string $indentation): self
16✔
721
    {
722
        $this->sIndentation = $indentation;
16✔
723

724
        return $this;
16✔
725
    }
726

727
    /**
728
     * @internal
729
     */
730
    public function getIgnoreExceptions(): bool
4✔
731
    {
732
        return $this->bIgnoreExceptions;
4✔
733
    }
734

735
    /**
736
     * @return $this fluent interface
737
     */
738
    public function setIgnoreExceptions(bool $ignoreExceptions): self
6✔
739
    {
740
        $this->bIgnoreExceptions = $ignoreExceptions;
6✔
741

742
        return $this;
6✔
743
    }
744

745
    /**
746
     * @internal
747
     */
748
    public function getRenderComments(): bool
3✔
749
    {
750
        return $this->bRenderComments;
3✔
751
    }
752

753
    /**
754
     * @return $this fluent interface
755
     */
756
    public function setRenderComments(bool $renderComments): self
7✔
757
    {
758
        $this->bRenderComments = $renderComments;
7✔
759

760
        return $this;
7✔
761
    }
762

763
    /**
764
     * @internal
765
     */
766
    public function getIndentationLevel(): int
22✔
767
    {
768
        return $this->iIndentationLevel;
22✔
769
    }
770

771
    /**
772
     * @return $this fluent interface
773
     */
774
    public function indentWithTabs(int $numberOfTabs = 1): self
6✔
775
    {
776
        return $this->setIndentation(\str_repeat("\t", $numberOfTabs));
6✔
777
    }
778

779
    /**
780
     * @return $this fluent interface
781
     */
782
    public function indentWithSpaces(int $numberOfSpaces = 2): self
7✔
783
    {
784
        return $this->setIndentation(\str_repeat(' ', $numberOfSpaces));
7✔
785
    }
786

787
    public function nextLevel(): self
23✔
788
    {
789
        if ($this->oNextLevelFormat === null) {
23✔
790
            $this->oNextLevelFormat = clone $this;
23✔
791
            $this->oNextLevelFormat->iIndentationLevel++;
23✔
792
            $this->oNextLevelFormat->oFormatter = null;
23✔
793
        }
794
        return $this->oNextLevelFormat;
23✔
795
    }
796

797
    public function beLenient(): void
1✔
798
    {
799
        $this->bIgnoreExceptions = true;
1✔
800
    }
1✔
801

802
    public function getFormatter(): OutputFormatter
22✔
803
    {
804
        if ($this->oFormatter === null) {
22✔
805
            $this->oFormatter = new OutputFormatter($this);
22✔
806
        }
807
        return $this->oFormatter;
22✔
808
    }
809

810
    /**
811
     * Creates an instance of this class without any particular formatting settings.
812
     */
813
    public static function create(): self
21✔
814
    {
815
        return new OutputFormat();
21✔
816
    }
817

818
    /**
819
     * Creates an instance of this class with a preset for compact formatting.
820
     */
821
    public static function createCompact(): self
3✔
822
    {
823
        $format = self::create();
3✔
824
        $format->set('Space*Rules', '')
3✔
825
            ->set('Space*Blocks', '')
3✔
826
            ->setSpaceAfterRuleName('')
3✔
827
            ->setSpaceBeforeOpeningBrace('')
3✔
828
            ->setSpaceAfterSelectorSeparator('')
3✔
829
            ->setRenderComments(false);
3✔
830
        return $format;
3✔
831
    }
832

833
    /**
834
     * Creates an instance of this class with a preset for pretty formatting.
835
     */
836
    public static function createPretty(): self
3✔
837
    {
838
        $format = self::create();
3✔
839
        $format->set('Space*Rules', "\n")
3✔
840
            ->set('Space*Blocks', "\n")
3✔
841
            ->setSpaceBetweenBlocks("\n\n")
3✔
842
            ->set('SpaceAfterListArgumentSeparators', [',' => ' '])
3✔
843
            ->setRenderComments(true);
3✔
844
        return $format;
3✔
845
    }
846
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc