or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli-interface.mdconfiguration.mdindex.mdplugin-system.mdprogrammatic-api.md
tile.json

plugin-system.mddocs/

0

# Plugin System

1

2

Karma's extensive plugin architecture enables customization of browsers, reporters, preprocessors, testing frameworks, and middleware. The plugin system uses dependency injection and provides standardized interfaces for extensibility.

3

4

## Capabilities

5

6

### Plugin Resolution

7

8

Load and resolve Karma plugins from various sources including npm packages, inline objects, and glob patterns.

9

10

```javascript { .api }

11

/**

12

* Resolve plugins from configuration

13

* @param plugins - Plugin specifications array

14

* @param emitter - Event emitter for plugin events

15

* @returns Array of resolved plugin modules

16

*/

17

function resolve(plugins: string[], emitter: EventEmitter): any[];

18

19

/**

20

* Create plugin instantiation function

21

* @param injector - Dependency injector instance

22

* @returns Function for instantiating plugins

23

*/

24

function createInstantiatePlugin(injector: any): Function;

25

```

26

27

**Plugin Resolution Examples:**

28

29

```javascript

30

// In karma.conf.js

31

module.exports = function(config) {

32

config.set({

33

plugins: [

34

// NPM package names

35

'karma-jasmine',

36

'karma-chrome-launcher',

37

'karma-coverage',

38

39

// Glob patterns

40

'karma-*',

41

42

// Scoped packages

43

'@angular-devkit/build-angular/plugins/karma',

44

45

// Inline plugin objects

46

{

47

'framework:custom': ['factory', function() {

48

// Custom framework implementation

49

}]

50

},

51

52

// Relative paths

53

'./custom-plugins/my-plugin.js'

54

]

55

});

56

};

57

```

58

59

### Plugin Types

60

61

Karma supports several plugin categories, each with specific interfaces and lifecycles.

62

63

```javascript { .api }

64

/**

65

* Plugin type definitions and interfaces

66

*/

67

interface PluginTypes {

68

// Browser launchers - launch and manage browser instances

69

'launcher': BrowserLauncher;

70

71

// Test result reporters - format and output test results

72

'reporter': Reporter;

73

74

// File preprocessors - transform files before serving

75

'preprocessor': Preprocessor;

76

77

// Testing frameworks - integrate test libraries

78

'framework': Framework;

79

80

// HTTP middleware - custom request handling

81

'middleware': Middleware;

82

}

83

```

84

85

### Browser Launchers

86

87

Create custom browser launchers for different environments and browser configurations.

88

89

```javascript { .api }

90

/**

91

* Browser launcher interface

92

*/

93

interface BrowserLauncher {

94

/**

95

* Start browser instance

96

* @param url - Test runner URL

97

* @param onExit - Exit callback

98

*/

99

start(url: string, onExit: Function): void;

100

101

/**

102

* Kill browser process

103

* @param done - Completion callback

104

*/

105

kill(done: Function): void;

106

107

/**

108

* Force kill browser process

109

* @returns Promise for completion

110

*/

111

forceKill(): Promise<void>;

112

113

/**

114

* Mark browser as captured

115

*/

116

markCaptured(): void;

117

118

/**

119

* Check if browser is captured

120

* @returns True if captured

121

*/

122

isCaptured(): boolean;

123

124

// Browser state properties

125

state: 'BEING_CAPTURED' | 'CAPTURED' | 'BEING_KILLED' | 'FINISHED' | 'RESTARTING';

126

id: string;

127

name: string;

128

displayName: string;

129

}

130

131

/**

132

* Base launcher decorator factory

133

*/

134

interface BaseLauncherDecorator {

135

(launcher: any): void;

136

}

137

```

138

139

**Custom Launcher Examples:**

140

141

```javascript

142

// Custom Chrome launcher with specific flags

143

module.exports = function(config) {

144

config.set({

145

customLaunchers: {

146

ChromeHeadlessCustom: {

147

base: 'ChromeHeadless',

148

flags: [

149

'--no-sandbox',

150

'--disable-web-security',

151

'--disable-features=VizDisplayCompositor',

152

'--remote-debugging-port=9222'

153

]

154

},

155

156

ChromeDebug: {

157

base: 'Chrome',

158

flags: ['--remote-debugging-port=9333'],

159

debug: true

160

},

161

162

FirefoxHeadlessCustom: {

163

base: 'FirefoxHeadless',

164

prefs: {

165

'network.proxy.type': 1,

166

'network.proxy.http': 'localhost',

167

'network.proxy.http_port': 9090

168

}

169

}

170

},

171

172

browsers: ['ChromeHeadlessCustom', 'FirefoxHeadlessCustom']

173

});

174

};

175

176

// Inline custom launcher plugin

177

const customLauncher = {

178

'launcher:CustomBrowser': ['type', function CustomBrowserLauncher() {

179

this.start = function(url) {

180

// Launch custom browser

181

};

182

183

this.kill = function(done) {

184

// Kill browser process

185

done();

186

};

187

}]

188

};

189

190

module.exports = function(config) {

191

config.set({

192

plugins: [customLauncher],

193

browsers: ['CustomBrowser']

194

});

195

};

196

```

197

198

### Reporters

199

200

Create custom reporters to format and output test results in different ways.

201

202

```javascript { .api }

203

/**

204

* Reporter interface

205

*/

206

interface Reporter {

207

/**

208

* Test run started

209

* @param browsers - Array of browser instances

210

*/

211

onRunStart(browsers: Browser[]): void;

212

213

/**

214

* Browser started

215

* @param browser - Browser instance

216

*/

217

onBrowserStart(browser: Browser): void;

218

219

/**

220

* Browser completed

221

* @param browser - Browser instance

222

* @param result - Test results

223

*/

224

onBrowserComplete(browser: Browser, result: BrowserResult): void;

225

226

/**

227

* Browser error

228

* @param browser - Browser instance

229

* @param error - Error object

230

*/

231

onBrowserError(browser: Browser, error: any): void;

232

233

/**

234

* Browser log message

235

* @param browser - Browser instance

236

* @param log - Log message

237

* @param type - Log type

238

*/

239

onBrowserLog(browser: Browser, log: string, type: string): void;

240

241

/**

242

* Individual test completed

243

* @param browser - Browser instance

244

* @param result - Test result

245

*/

246

onSpecComplete(browser: Browser, result: TestResult): void;

247

248

/**

249

* Test run completed

250

* @param browsers - Array of browser instances

251

* @param results - Overall results

252

*/

253

onRunComplete(browsers: Browser[], results: TestResults): void;

254

255

// Optional methods

256

write?(chunk: string): void;

257

writeCommonMsg?(msg: string): void;

258

onExit?(done: Function): void;

259

}

260

261

/**

262

* Base reporter with utility methods

263

*/

264

class BaseReporter implements Reporter {

265

write(chunk: string): void;

266

renderBrowser(browser: Browser): string;

267

specSuccess(browser: Browser, result: TestResult): void;

268

specFailure(browser: Browser, result: TestResult): void;

269

specSkipped(browser: Browser, result: TestResult): void;

270

}

271

```

272

273

**Custom Reporter Examples:**

274

275

```javascript

276

// Custom JSON reporter

277

const jsonReporter = function(baseReporterDecorator, config, logger, helper) {

278

baseReporterDecorator(this);

279

280

const log = logger.create('reporter.json');

281

const reporterConfig = config.jsonReporter || {};

282

const outputFile = reporterConfig.outputFile || 'test-results.json';

283

284

let results = {

285

browsers: {},

286

summary: {},

287

tests: []

288

};

289

290

this.onRunStart = function(browsers) {

291

results.timestamp = new Date().toISOString();

292

results.browsers = browsers.map(b => ({

293

id: b.id,

294

name: b.name,

295

fullName: b.fullName

296

}));

297

};

298

299

this.onSpecComplete = function(browser, result) {

300

results.tests.push({

301

browser: browser.name,

302

suite: result.suite,

303

description: result.description,

304

success: result.success,

305

time: result.time,

306

log: result.log

307

});

308

};

309

310

this.onRunComplete = function(browsers, summary) {

311

results.summary = summary;

312

313

helper.mkdirIfNotExists(path.dirname(outputFile), () => {

314

fs.writeFileSync(outputFile, JSON.stringify(results, null, 2));

315

log.info('Test results written to %s', outputFile);

316

});

317

};

318

};

319

320

jsonReporter.$inject = ['baseReporterDecorator', 'config', 'logger', 'helper'];

321

322

module.exports = {

323

'reporter:json': ['type', jsonReporter]

324

};

325

```

326

327

### Preprocessors

328

329

Transform files before they are served to browsers, enabling transpilation, bundling, and instrumentation.

330

331

```javascript { .api }

332

/**

333

* Preprocessor interface

334

*/

335

interface Preprocessor {

336

/**

337

* Process file content

338

* @param content - Original file content

339

* @param file - File object with metadata

340

* @param done - Completion callback with (error, content)

341

*/

342

(content: string, file: File, done: (error?: any, content?: string) => void): void;

343

}

344

345

/**

346

* File object passed to preprocessors

347

*/

348

interface File {

349

originalPath: string; // Original file path

350

path: string; // Processed file path

351

contentPath: string; // Path to content file

352

mtime: Date; // Modification time

353

isUrl: boolean; // Is external URL

354

isBinary: boolean; // Is binary file

355

}

356

```

357

358

**Custom Preprocessor Examples:**

359

360

```javascript

361

// Simple string replacement preprocessor

362

const stringReplace = function(args, config, logger) {

363

const log = logger.create('preprocessor.string-replace');

364

const options = args.options || {};

365

366

return function(content, file, done) {

367

log.debug('Processing "%s".', file.originalPath);

368

369

let result = content;

370

371

// Apply string replacements

372

if (options.replacements) {

373

options.replacements.forEach(replacement => {

374

result = result.replace(replacement.from, replacement.to);

375

});

376

}

377

378

done(null, result);

379

};

380

};

381

382

stringReplace.$inject = ['args', 'config', 'logger'];

383

384

// ES6 to ES5 transpiler preprocessor

385

const babelPreprocessor = function(args, config, logger) {

386

const babel = require('@babel/core');

387

const log = logger.create('preprocessor.babel');

388

389

return function(content, file, done) {

390

log.debug('Processing "%s".', file.originalPath);

391

392

try {

393

const result = babel.transform(content, {

394

filename: file.originalPath,

395

presets: ['@babel/preset-env'],

396

sourceMaps: 'inline'

397

});

398

399

done(null, result.code);

400

} catch (error) {

401

log.error('Error processing "%s": %s', file.originalPath, error.message);

402

done(error);

403

}

404

};

405

};

406

407

babelPreprocessor.$inject = ['args', 'config', 'logger'];

408

409

module.exports = {

410

'preprocessor:string-replace': ['factory', stringReplace],

411

'preprocessor:babel': ['factory', babelPreprocessor]

412

};

413

```

414

415

### Testing Frameworks

416

417

Integrate different testing libraries and frameworks with Karma.

418

419

```javascript { .api }

420

/**

421

* Framework interface

422

*/

423

interface Framework {

424

/**

425

* Initialize framework on client side

426

* @param files - File list to modify

427

*/

428

(files: any[]): void;

429

}

430

```

431

432

**Framework Plugin Examples:**

433

434

```javascript

435

// Custom testing framework

436

const customFramework = function(files) {

437

// Add framework files to the file list

438

files.unshift({

439

pattern: require.resolve('./custom-framework.js'),

440

included: true,

441

served: true,

442

watched: false

443

});

444

445

// Add adapter file

446

files.unshift({

447

pattern: require.resolve('./custom-adapter.js'),

448

included: true,

449

served: true,

450

watched: false

451

});

452

};

453

454

customFramework.$inject = ['config.files'];

455

456

module.exports = {

457

'framework:custom': ['factory', customFramework]

458

};

459

```

460

461

### Middleware

462

463

Add custom HTTP middleware to the Karma server for handling specific requests.

464

465

```javascript { .api }

466

/**

467

* Middleware interface

468

*/

469

interface Middleware {

470

/**

471

* Express-style middleware function

472

* @param req - HTTP request

473

* @param res - HTTP response

474

* @param next - Next middleware function

475

*/

476

(req: any, res: any, next: Function): void;

477

}

478

```

479

480

**Middleware Examples:**

481

482

```javascript

483

// API endpoint middleware

484

const apiMiddleware = function(config) {

485

return function(req, res, next) {

486

if (req.url.startsWith('/api/')) {

487

// Handle API requests

488

res.setHeader('Content-Type', 'application/json');

489

res.end(JSON.stringify({ message: 'API response' }));

490

} else {

491

next();

492

}

493

};

494

};

495

496

apiMiddleware.$inject = ['config'];

497

498

// CORS middleware

499

const corsMiddleware = function() {

500

return function(req, res, next) {

501

res.setHeader('Access-Control-Allow-Origin', '*');

502

res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');

503

res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');

504

505

if (req.method === 'OPTIONS') {

506

res.end();

507

} else {

508

next();

509

}

510

};

511

};

512

513

module.exports = {

514

'middleware:api': ['factory', apiMiddleware],

515

'middleware:cors': ['factory', corsMiddleware]

516

};

517

```

518

519

## Built-in Plugins

520

521

Karma includes several built-in plugins and supports many community plugins.

522

523

### Popular Browser Launchers

524

525

```javascript { .api }

526

// Built-in and popular browser launchers

527

'karma-chrome-launcher' // Google Chrome

528

'karma-firefox-launcher' // Mozilla Firefox

529

'karma-safari-launcher' // Safari

530

'karma-ie-launcher' // Internet Explorer

531

'karma-edge-launcher' // Microsoft Edge

532

'karma-phantomjs-launcher' // PhantomJS (deprecated)

533

'karma-browserstack-launcher' // BrowserStack

534

'karma-sauce-launcher' // Sauce Labs

535

```

536

537

### Popular Reporters

538

539

```javascript { .api }

540

// Built-in and popular reporters

541

'karma-progress-reporter' // Progress dots (built-in)

542

'karma-dots-reporter' // Simple dots (built-in)

543

'karma-junit-reporter' // JUnit XML

544

'karma-coverage-reporter' // Code coverage

545

'karma-spec-reporter' // Detailed spec output

546

'karma-json-reporter' // JSON output

547

'karma-teamcity-reporter' // TeamCity integration

548

```

549

550

### Popular Preprocessors

551

552

```javascript { .api }

553

// Popular preprocessors

554

'karma-babel-preprocessor' // Babel transpilation

555

'karma-webpack' // Webpack bundling

556

'karma-rollup-preprocessor' // Rollup bundling

557

'karma-browserify' // Browserify bundling

558

'karma-typescript' // TypeScript compilation

559

'karma-coverage' // Code coverage instrumentation

560

```

561

562

### Popular Frameworks

563

564

```javascript { .api }

565

// Popular testing frameworks

566

'karma-jasmine' // Jasmine

567

'karma-mocha' // Mocha

568

'karma-qunit' // QUnit

569

'karma-tap' // TAP

570

'karma-jest' // Jest compatibility

571

```