diff --git a/.gitignore b/.gitignore index 03902db6..a98bb0c2 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,6 @@ /node_modules /packages/**/dist /package-lock.json -/lib/runtime +/lib/packages /.env /scripts/mock/demo/dist/ diff --git a/lib/generate-data.js b/lib/generate-data.js index bcd0b206..589c37c4 100644 --- a/lib/generate-data.js +++ b/lib/generate-data.js @@ -4,7 +4,7 @@ const Util = require('./utils/util.js'); const { getTickInfo } = require('./utils/system.js'); const Visitor = require('./visitor.js'); const { calculateSummary } = require('./common.js'); -const { addGlobalCoverageReport } = require('./plugins/coverage/coverage.js'); +const { generateGlobalCoverageReport } = require('./plugins/coverage/coverage.js'); const version = require('../package.json').version; const getReportName = (options, config, metadata) => { @@ -15,27 +15,15 @@ const getReportName = (options, config, metadata) => { return 'Test Report'; }; -const generateGlobalCoverageReport = (dataList, options) => { - const { - name, outputDir, coverage - } = options; - const coverageOptions = { - title: `Coverage Report - ${name}`, - outputDir, - outputName: 'coverage', - ... coverage - }; - return addGlobalCoverageReport(dataList, coverageOptions); -}; - -const artifactsHandler = async (visitor, options) => { +const artifactsHandler = async (visitor, reporterOptions) => { const artifacts = [].concat(visitor.artifacts); + // global artifacts - const { coverage } = visitor.artifactDataMap; - if (coverage) { - const report = await generateGlobalCoverageReport(coverage, options); - artifacts.push(report); + const globalCoverageReport = await generateGlobalCoverageReport(reporterOptions); + if (globalCoverageReport) { + artifacts.push(globalCoverageReport); } + return artifacts; }; diff --git a/lib/generate-report.js b/lib/generate-report.js index 7a075bec..b3d7b106 100644 --- a/lib/generate-report.js +++ b/lib/generate-report.js @@ -17,16 +17,19 @@ const generateJson = (outputDir, htmlFile, reportData) => { const generateHtml = async (outputDir, htmlFile, reportData, inline) => { + const jsFiles = []; + jsFiles.push(path.resolve(__dirname, './packages/monocart-common.js')); + jsFiles.push(path.resolve(__dirname, './packages/monocart-reporter.js')); + const options = { inline, reportData, - jsFiles: ['monocart-common.js', 'monocart-reporter.js'], - htmlDir: outputDir, + jsFiles, + assetsPath: './assets', + outputDir, htmlFile, - outputDir, - reportDataFile: 'report-data.js', - assetsRelative: '' + reportDataFile: 'report-data.js' }; const htmlPath = await Util.saveHtmlReport(options); @@ -152,7 +155,7 @@ const generateReport = async (reportData, options) => { if (artifacts) { artifacts.forEach((report) => { const g = report.global ? `${EC.magenta('(global)')}` : ''; - Util.logInfo(`${report.name}: ${EC.cyan(report.path)} ${report.title} ${g}`); + Util.logInfo(`${report.type}: ${EC.cyan(report.path)} ${report.name} ${g}`); // convert path to relative reporter report.path = Util.relativePath(report.path, outputDir); }); diff --git a/lib/index.d.ts b/lib/index.d.ts index 88dcbbb4..955c8065 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -70,15 +70,9 @@ export type CoverageReportOptions = { export function addCoverageReport( v8list: any[], - testInfo: TestInfo, - options?: CoverageReportOptions + testInfo: TestInfo ): Promise; -export function addGlobalCoverageReport( - dataList: any[], - options: CoverageReportOptions -): Promise; - export function attachCoverageReport( coverage: any[] | any, testInfo: TestInfo, diff --git a/lib/index.js b/lib/index.js index c23ff320..31b581f0 100644 --- a/lib/index.js +++ b/lib/index.js @@ -7,9 +7,7 @@ const { getTrends } = require('./common.js'); const merge = require('./merge-data.js'); const { attachAuditReport } = require('./plugins/audit/audit.js'); -const { - addCoverageReport, addGlobalCoverageReport, attachCoverageReport -} = require('./plugins/coverage/coverage.js'); +const { addCoverageReport, attachCoverageReport } = require('./plugins/coverage/coverage.js'); const { attachNetworkReport } = require('./plugins/network/network.js'); const { createStateServer, useState } = require('./plugins/state/state.js'); @@ -24,7 +22,6 @@ class Reporter { static attachAuditReport = attachAuditReport; static addCoverageReport = addCoverageReport; - static addGlobalCoverageReport = addGlobalCoverageReport; static attachCoverageReport = attachCoverageReport; static attachNetworkReport = attachNetworkReport; diff --git a/lib/index.mjs b/lib/index.mjs index 0365a631..8f52b1cb 100644 --- a/lib/index.mjs +++ b/lib/index.mjs @@ -8,7 +8,6 @@ export const merge = MonocartReporter.merge; export const attachAuditReport = MonocartReporter.attachAuditReport; export const addCoverageReport = MonocartReporter.addCoverageReport; -export const addGlobalCoverageReport = MonocartReporter.addGlobalCoverageReport; export const attachCoverageReport = MonocartReporter.attachCoverageReport; export const attachNetworkReport = MonocartReporter.attachNetworkReport; diff --git a/lib/platform/share.js b/lib/platform/share.js index 8028402e..ffa1e908 100644 --- a/lib/platform/share.js +++ b/lib/platform/share.js @@ -19,11 +19,6 @@ const Util = { name: 'network', contentType: 'text/html', reportFile: 'network-report.json' - }, - // artifact will be removed finally - artifact: { - name: 'artifact', - contentType: 'application/json' } }, diff --git a/lib/plugins/coverage/converter/collect-source-maps.js b/lib/plugins/coverage/converter/collect-source-maps.js deleted file mode 100644 index 21b697da..00000000 --- a/lib/plugins/coverage/converter/collect-source-maps.js +++ /dev/null @@ -1,194 +0,0 @@ -const fs = require('fs'); -const EC = require('eight-colors'); -const { fileURLToPath } = require('url'); -const Concurrency = require('../../../platform/concurrency.js'); -const { convertSourceMap, axios } = require('../../../runtime/monocart-coverage.js'); - -const Util = require('../../../utils/util.js'); - -const request = async (options) => { - if (typeof options === 'string') { - options = { - url: options - }; - } - let err; - const res = await axios(options).catch((e) => { - err = e; - }); - return [err, res]; -}; - -const loadSourceMap = async (url = '') => { - - if (url.startsWith('file://')) { - const p = fileURLToPath(url); - const json = Util.readJSONSync(p); - if (!json) { - Util.logDebug(EC.red(`failed to load sourcemap ${p}`)); - return; - } - return json; - } - - const [err, res] = await request({ - url - }); - - if (err) { - Util.logDebug(EC.red(`${err.message} ${url}`)); - return; - } - - return res.data; -}; - -const getSourceMapUrl = (content, url) => { - - const m = content.match(convertSourceMap.mapFileCommentRegex); - if (!m) { - return; - } - - const comment = m.pop(); - const r = convertSourceMap.mapFileCommentRegex.exec(comment); - // for some odd reason //# .. captures in 1 and /* .. */ in 2 - const filename = r[1] || r[2]; - - let mapUrl; - - try { - mapUrl = new URL(filename, url); - } catch (e) { - Util.logDebug(EC.red(`${e.message} ${filename} ${url}`)); - } - if (mapUrl) { - return mapUrl.toString(); - } -}; - -const resolveSourceMap = (data) => { - if (data) { - const { - sources, sourcesContent, mappings - } = data; - if (!sources || !sourcesContent || !mappings) { - return; - } - return data; - } -}; - -const collectInlineSourceMaps = async (v8list) => { - let count = 0; - const concurrency = new Concurrency(); - for (const item of v8list) { - - const { type, source } = item; - - // only for js - if (type === 'js') { - const converter = convertSourceMap.fromSource(source); - if (converter) { - item.sourceMap = resolveSourceMap(converter.sourcemap); - count += 1; - // source map comments is inline source map, remove it because it is big - item.source = convertSourceMap.removeComments(source); - - continue; - } - const sourceMapUrl = getSourceMapUrl(source, item.url); - if (sourceMapUrl) { - // console.log('========================', sourceMapUrl); - item.sourceMapUrl = sourceMapUrl; - concurrency.addItem(item); - } - } - } - await concurrency.start(async (item) => { - const data = await loadSourceMap(item.sourceMapUrl); - if (data) { - item.sourceMap = resolveSourceMap(data); - count += 1; - // source map comments is a link, no need remove - } - }); - - return count; -}; - -const collectFileSourceMaps = async (v8list, options) => { - let count = 0; - const concurrency = new Concurrency(); - for (const item of v8list) { - - const { - type, url, source, id - } = item; - - // remove source just keep functions to reduce artifacts size - delete item.source; - - const sourcePath = Util.resolveArtifactSourcePath(options.artifactsDir, id); - if (fs.existsSync(sourcePath)) { - continue; - } - - const sourceData = { - url, - id, - source: convertSourceMap.removeComments(source) - }; - - // only for js - if (type === 'js') { - const converter = convertSourceMap.fromSource(source); - if (converter) { - sourceData.sourceMap = resolveSourceMap(converter.sourcemap); - count += 1; - await saveSourceFile(sourcePath, sourceData); - continue; - } - const sourceMapUrl = getSourceMapUrl(source, item.url); - if (sourceMapUrl) { - concurrency.addItem({ - sourceMapUrl, - sourcePath, - sourceData - }); - continue; - } - } - - await saveSourceFile(sourcePath, sourceData); - - } - - await concurrency.start(async (item) => { - const sourceData = item.sourceData; - const data = await loadSourceMap(item.sourceMapUrl); - if (data) { - sourceData.sourceMap = resolveSourceMap(data); - count += 1; - } - await saveSourceFile(item.sourcePath, sourceData); - }); - - return count; -}; - -const saveSourceFile = async (filePath, data) => { - await Util.writeFile(filePath, JSON.stringify(data)); -}; - -const collectSourceMaps = (v8list, options, inlineSourceMap) => { - - if (inlineSourceMap) { - return collectInlineSourceMaps(v8list); - } - - return collectFileSourceMaps(v8list, options); - -}; - -module.exports = collectSourceMaps; diff --git a/lib/plugins/coverage/converter/converter.js b/lib/plugins/coverage/converter/converter.js deleted file mode 100644 index 26d2d68c..00000000 --- a/lib/plugins/coverage/converter/converter.js +++ /dev/null @@ -1,565 +0,0 @@ -const path = require('path'); - -const Util = require('../../../utils/util.js'); -const decodeMappings = require('../../../utils/decode-mappings.js'); - -// position mapping for conversion between offset and line/column -const PositionMapping = require('./position-mapping.js'); -const findOriginalRange = require('./find-original-range.js'); - -const { dedupeCountRanges } = require('./dedupe.js'); -const { getSourceType, initSourceMapSourcePath } = require('./source-path.js'); - -const InfoLine = require('./info-line.js'); -const InfoBranch = require('./info-branch.js'); -const InfoFunction = require('./info-function.js'); - -// ======================================================================================================== - -// istanbul coverage format -/** - * * `path` - the file path for which coverage is being tracked - * * `statementMap` - map of statement locations keyed by statement index - * * `fnMap` - map of function metadata keyed by function index - * * `branchMap` - map of branch metadata keyed by branch index - * * `s` - hit counts for statements - * * `f` - hit count for functions - * * `b` - hit count for branches - */ -const getFileCoverage = (sourcePath, inputData) => { - - const { - lines, functions, branches, ranges - } = inputData; - - // v8 ranges - inputData.ranges = dedupeCountRanges(ranges); - - // istanbul coverage - const coverage = { - path: sourcePath, - - statementMap: {}, - s: {}, - - fnMap: {}, - f: {}, - - branchMap: {}, - b: {} - }; - - lines.forEach((line, index) => { - coverage.statementMap[`${index}`] = line.generate(); - coverage.s[`${index}`] = line.count; - }); - - functions.forEach((fn, index) => { - coverage.fnMap[`${index}`] = fn.generate(); - coverage.f[`${index}`] = fn.count; - }); - - branches.forEach((branch, index) => { - coverage.branchMap[`${index}`] = branch.generate(); - coverage.b[`${index}`] = [branch.count]; - }); - - return coverage; -}; - -// ======================================================================================================== - -const setLineCount = (lineMap, line, count) => { - const lineInfo = lineMap[line]; - if (lineInfo) { - lineInfo.count = count; - } -}; - -const setSingleLineCount = (lineMap, sLoc, eLoc, count) => { - // nothing between - if (sLoc.column >= eLoc.column) { - return; - } - - // sometimes column > length - if (sLoc.column <= sLoc.indent && eLoc.column >= eLoc.length) { - // console.log('single', sLoc.line); - setLineCount(lineMap, sLoc.line, count); - } - -}; - -const updateLinesCount = (lineMap, sLoc, eLoc, count) => { - - // single line - if (sLoc.line === eLoc.line) { - setSingleLineCount(lineMap, sLoc, eLoc, count); - return; - } - - const firstELoc = { - ... sLoc, - column: sLoc.length - }; - setSingleLineCount(lineMap, sLoc, firstELoc, count); - - for (let i = sLoc.line + 1; i < eLoc.line; i++) { - setLineCount(lineMap, i, count); - } - - const lastSLoc = { - ... eLoc, - column: eLoc.indent - }; - setSingleLineCount(lineMap, lastSLoc, eLoc, count); - -}; - -const initFileCoverage = (positionMapping, count) => { - - // istanbul - const lines = []; - const functions = []; - const branches = []; - // v8 - const ranges = []; - - // add all lines - const lineMap = {}; - - const { commentedLines, blankLines } = positionMapping; - positionMapping.lines.forEach((it) => { - // exclude comments and blanks - if (commentedLines.includes(it.line) || blankLines.includes(it.line)) { - return; - } - // line 1-base - const line = it.line + 1; - const lineInfo = new InfoLine(line, it.length, count); - lineMap[line] = lineInfo; - lines.push(lineInfo); - }); - - return { - lines, functions, branches, ranges, lineMap - }; -}; - - -// https://github.com/demurgos/v8-coverage -/** - * @ranges is always non-empty. The first range is called the "root range". - * @isBlockCoverage indicates if the function has block coverage information - * @false means that there is a single range and its count is the number of times the function was called. - * @true means that the ranges form a tree of blocks representing how many times each statement or expression inside was executed. - * It detects skipped or repeated statements. The root range counts the number of function calls. - * - * @functionName can be an empty string. This is common for the FunctionCov representing the whole module. - */ -const addJsCoverage = (coverage, block, range, index, positionMapping) => { - - const { - functions, branches, ranges, lineMap - } = coverage; - - const { isBlockCoverage, functionName } = block; - const { - startOffset, endOffset, count - } = range; - - ranges.push({ - start: startOffset, - end: endOffset, - count - }); - - const sLoc = positionMapping.offsetToLocation(startOffset); - const eLoc = positionMapping.offsetToLocation(endOffset); - - // line, column - updateLinesCount(lineMap, sLoc, eLoc, count); - - if (isBlockCoverage) { - if (index === 0) { - // The root range counts the number of function calls - functions.push(new InfoFunction(sLoc, eLoc, count, functionName)); - } - - // index 0 not really a branch, but for covered whole function - branches.push(new InfoBranch(sLoc, eLoc, count)); - - } else { - functions.push(new InfoFunction(sLoc, eLoc, count, functionName)); - - // possible have branches in the function but no information for it - - } -}; - -const addCssCoverage = (coverage, range, positionMapping) => { - const { lineMap } = coverage; - - const { start, end } = range; - - const sLoc = positionMapping.offsetToLocation(start); - const eLoc = positionMapping.offsetToLocation(end); - - // line, column - updateLinesCount(lineMap, sLoc, eLoc, 1); - -}; - -// ======================================================================================================== - -const getDistCoverage = (item, state) => { - const positionMapping = state.positionMapping; - if (item.type === 'js') { - const coverage = initFileCoverage(positionMapping, 1); - item.functions.forEach((block) => { - block.ranges.forEach((range, index) => { - addJsCoverage(coverage, block, range, index, positionMapping); - }); - }); - return coverage; - } - - const coverage = initFileCoverage(positionMapping, 0); - item.ranges.forEach((range) => { - addCssCoverage(coverage, range, positionMapping); - }); - return coverage; -}; - -const unpackDistSource = (item, state) => { - const coverage = getDistCoverage(item, state); - const sourcePath = item.sourcePath; - state.coverageData[sourcePath] = getFileCoverage(sourcePath, coverage); - // after dedupe - item.ranges = coverage.ranges; -}; - -// ======================================================================================================== - -const decodeSourceMappings = async (sourceMap, generatedPositionMapping) => { - - const decodedList = await decodeMappings(sourceMap.mappings); - - const originalIndexMap = new Map(); - sourceMap.sources.forEach((item, i) => { - originalIndexMap.set(i, []); - }); - - const allDecodedMappings = []; - let generatedIndex = 0; - decodedList.forEach((segments, generatedLine) => { - let item = null; - segments.forEach((segment) => { - const [generatedColumn, sourceIndex, originalLine, originalColumn] = segment; - const generatedOffset = generatedPositionMapping.locationToOffset({ - // 1-base - line: generatedLine + 1, - column: generatedColumn - }); - - item = { - generatedOffset, - generatedLine, - generatedColumn, - generatedIndex, - - sourceIndex, - originalLine, - originalColumn - }; - - allDecodedMappings.push(item); - generatedIndex += 1; - - if (typeof sourceIndex === 'undefined') { - return; - } - - originalIndexMap.get(sourceIndex).push(item); - - }); - - // line last one - if (item) { - const line = generatedPositionMapping.getLine(item.generatedLine + 1); - // last column - item.generatedEndOffset = item.generatedOffset + (line.length - item.generatedColumn); - } - - }); - - // defaults to sort by generated offset, not need sort - // allDecodedMappings.sort((a, b) => { - // return a.generatedOffset - b.generatedOffset; - // }); - - return { - allDecodedMappings, - originalIndexMap - }; - -}; - -const getOriginalDecodedMappings = (originalIndexMap, sourceIndex, positionMapping) => { - // all mappings for the original file sorted - const decodedMappings = originalIndexMap.get(sourceIndex); - - if (!decodeMappings) { - return []; - } - - // sort by original line/column - decodedMappings.sort((a, b) => { - if (a.originalLine === b.originalLine) { - return a.originalColumn - b.originalColumn; - } - return a.originalLine - b.originalLine; - }); - - // add offset and index - decodedMappings.forEach((item, i) => { - item.originalIndex = i; - item.originalOffset = positionMapping.locationToOffset({ - line: item.originalLine + 1, - column: item.originalColumn - }); - }); - - return decodedMappings; -}; - -const initOriginalList = (sourceMap, originalIndexMap, fileSources, options) => { - - // source filter - const { sources, sourcesContent } = sourceMap; - - let sourceFilter = options.sourceFilter; - if (typeof sourceFilter !== 'function') { - sourceFilter = () => true; - } - - // create original content mappings - const map = new Map(); - - sources.forEach((sourcePath, sourceIndex) => { - - // filter - if (!sourceFilter(sourcePath)) { - return; - } - - // console.log(`add source: ${k}`); - const sourceContent = sourcesContent[sourceIndex]; - if (typeof sourceContent !== 'string') { - Util.logError(`not found source content: ${sourcePath}`); - return; - } - - // keep original formatted content - fileSources[sourcePath] = sourceContent; - - const positionMapping = new PositionMapping(sourceContent); - - const decodedMappings = getOriginalDecodedMappings(originalIndexMap, sourceIndex, positionMapping); - - const coverage = initFileCoverage(positionMapping, 1); - - const type = getSourceType(sourcePath); - - const originalState = { - source: sourceContent, - type, - sourcePath, - positionMapping, - decodedMappings, - coverage - }; - - map.set(sourceIndex, originalState); - }); - - return map; -}; - -const unpackSourceMap = async (item, state, options) => { - const sourceMap = item.sourceMap; - const generatedPositionMapping = state.positionMapping; - const distFile = sourceMap.file || path.basename(item.sourcePath); - - // keep original urls - const fileUrls = {}; - initSourceMapSourcePath(sourceMap, fileUrls, options.sourcePath); - - // decode mappings for each original file - const time_start_decode = Date.now(); - const { allDecodedMappings, originalIndexMap } = await decodeSourceMappings(sourceMap, generatedPositionMapping); - // only debug level - Util.logTime(`decode source mappings ${distFile}`, time_start_decode); - - // filter original list and init list - const fileSources = state.fileSources; - const originalMap = initOriginalList(sourceMap, originalIndexMap, fileSources, options); - - originalIndexMap.clear(); - - const generatedState = { - decodedMappings: allDecodedMappings, - positionMapping: generatedPositionMapping - }; - - // const time_start_mapping = Date.now(); - item.functions.forEach((block) => { - block.ranges.forEach((range, index) => { - - const result = findOriginalRange(range, generatedState, originalMap); - if (!result) { - return; - } - - const { originalRange, originalState } = result; - const { coverage, positionMapping } = originalState; - addJsCoverage(coverage, block, originalRange, index, positionMapping); - - }); - }); - - // collect original files - const sourceList = []; - originalMap.forEach((originalState) => { - const { - source, type, sourcePath, coverage - } = originalState; - - // generate coverage - state.coverageData[sourcePath] = getFileCoverage(sourcePath, coverage); - const ranges = coverage.ranges; - - // add file item - const url = fileUrls[sourcePath] || sourcePath; - const id = Util.calculateSha1(url + source); - - const sourceItem = { - url, - id, - type, - sourcePath, - distFile, - ranges, - source - }; - - sourceList.push(sourceItem); - }); - - state.sourceList = sourceList; - -}; - -// ======================================================================================================== - -const unpackDistFile = async (item, state, options) => { - - if (item.sourceMap) { - if (Util.loggingType === 'debug') { - // js self - unpackDistSource(item, state, options); - } else { - item.dedupe = true; - } - - // unpack source map - await unpackSourceMap(item, state, options); - - // remove sourceMap - delete item.sourceMap; - - } else { - - // css/js self - unpackDistSource(item, state, options); - - } - - // clean after all - delete item.functions; - -}; - -// ======================================================================================================== - -const dedupeV8List = (v8list) => { - const indexes = []; - v8list.forEach((item, i) => { - if (item.dedupe) { - indexes.push(i); - } - }); - if (indexes.length) { - indexes.reverse(); - indexes.forEach((i) => { - v8list.splice(i, 1); - }); - } -}; - - -const convertV8List = async (v8list, options) => { - - // global file sources and coverage data - const fileSources = {}; - const coverageData = {}; - let sourceList = []; - - for (const item of v8list) { - // console.log([item.id]); - - const { source, sourcePath } = item; - - const positionMapping = new PositionMapping(source); - - // append file source - fileSources[sourcePath] = source; - - // current file and it's sources from sourceMap - const state = { - fileSources: {}, - coverageData: {}, - positionMapping - }; - - await unpackDistFile(item, state, options); - - // merge state - Object.assign(fileSources, state.fileSources); - Object.assign(coverageData, state.coverageData); - if (state.sourceList) { - sourceList = sourceList.concat(state.sourceList); - } - - } - - // dedupe - dedupeV8List(v8list); - - // add all sources - if (sourceList.length) { - sourceList.forEach((item) => { - v8list.push(item); - }); - } - - return { - fileSources, - coverageData - }; - -}; - -module.exports = { - convertV8List -}; diff --git a/lib/plugins/coverage/converter/dedupe.js b/lib/plugins/coverage/converter/dedupe.js deleted file mode 100644 index 67d47b50..00000000 --- a/lib/plugins/coverage/converter/dedupe.js +++ /dev/null @@ -1,110 +0,0 @@ - -const sortRanges = (ranges) => { - ranges.sort((a, b) => { - if (a.start === b.start) { - return a.end - b.end; - } - return a.start - b.start; - }); -}; - -const filterRanges = (ranges) => { - // remove start = end - return ranges.filter((range) => range.start < range.end); -}; - -// apply directly to css ranges -const dedupeRanges = (ranges) => { - - ranges = filterRanges(ranges); - - if (ranges.length < 2) { - return ranges; - } - - sortRanges(ranges); - - ranges.reduce((prevRange, range) => { - - // same start - if (range.start === prevRange.start) { - range.dedupe = true; - // equal prev - if (range.end === prevRange.end) { - return prevRange; - } - // great than the prev end, update the end - prevRange.end = range.end; - return prevRange; - } - - // already in the range - if (range.end <= prevRange.end) { - range.dedupe = true; - return prevRange; - } - - // collected, update the end - if (range.start <= prevRange.end) { - range.dedupe = true; - prevRange.end = range.end; - return prevRange; - } - - return range; - }); - - ranges = ranges.filter((it) => !it.dedupe); - - return ranges; -}; - -// apply to js count ranges -const dedupeCountRanges = (ranges) => { - - // v8 ranges format - // { startOffset: 0, endOffset: 6, count: 0 }, - // { startOffset: 0, endOffset: 6, count: 1 }, - // { startOffset: 0, endOffset: 297, count: 1 } - - // count ranges format - // { start: 0, end: 6, count: 0 } - - ranges = filterRanges(ranges); - - if (ranges.length < 2) { - return ranges; - } - - sortRanges(ranges); - - let hasDedupe = false; - - // merge count for same range - ranges.reduce((lastRange, range) => { - if (range.start === lastRange.start && range.end === lastRange.end) { - range.dedupe = true; - lastRange.count += range.count; - - hasDedupe = true; - - return lastRange; - } - return range; - }); - - if (hasDedupe) { - // console.log(ranges); - ranges = ranges.filter((it) => !it.dedupe); - } - - // console.log('ranges length after', ranges.length); - - return ranges; -}; - - -module.exports = { - dedupeRanges, - dedupeCountRanges -}; diff --git a/lib/plugins/coverage/converter/find-original-range.js b/lib/plugins/coverage/converter/find-original-range.js deleted file mode 100644 index e3737fa0..00000000 --- a/lib/plugins/coverage/converter/find-original-range.js +++ /dev/null @@ -1,581 +0,0 @@ - -const findMapping = (list, offset) => { - let start = 0; - let end = list.length - 1; - while (end - start > 1) { - const i = Math.floor((start + end) * 0.5); - const item = list[i]; - if (offset < item.generatedOffset) { - end = i; - continue; - } - if (offset > item.generatedOffset) { - start = i; - continue; - } - return list[i]; - } - // last two items, less is start - const endItem = list[end]; - if (offset < endItem.generatedOffset) { - return list[start]; - } - return list[end]; - -}; - -const findOffsetMapping = (generatedState, offset) => { - - const decodedMappings = generatedState.decodedMappings; - - // possible no length - if (!decodedMappings.length) { - return; - } - - const mapping = findMapping(decodedMappings, offset); - - const generatedOffset = mapping.generatedOffset; - - // not found, allow > last, not allow < first - if (offset < generatedOffset) { - return; - } - - // exact matched no need fix - const exact = generatedOffset === offset; - - return { - ... mapping, - // could be fixed if not exact matched - column: mapping.originalColumn, - exact - }; -}; - -const findNextOriginalDiffMapping = (originalState, mapping) => { - const decodedMappings = originalState.decodedMappings; - const { originalIndex, originalOffset } = mapping; - - let i = originalIndex + 1; - const l = decodedMappings.length; - while (i < l) { - const item = decodedMappings[i]; - // sometimes next is same line/column - if (item.originalOffset > originalOffset) { - return item; - } - i += 1; - } -}; - -const findNextGeneratedDiffMapping = (generatedState, mapping) => { - const decodedMappings = generatedState.decodedMappings; - const i = mapping.generatedIndex + 1; - const l = decodedMappings.length; - if (i < l) { - return decodedMappings[i]; - } -}; - -const getGeneratedText = (mapping, generatedState) => { - - const generatedText = mapping.generatedText; - if (typeof generatedText === 'string') { - return generatedText; - } - - let text = ''; - - const { positionMapping } = generatedState; - const nextMapping = findNextGeneratedDiffMapping(generatedState, mapping); - if (nextMapping) { - text = positionMapping.getSlice(mapping.generatedOffset, nextMapping.generatedOffset); - } else { - // to the end - text = positionMapping.getSlice(mapping.generatedOffset); - } - - // never cross line - if (mapping.generatedEndOffset) { - const len = mapping.generatedEndOffset - mapping.generatedOffset; - text = text.slice(0, len); - } - - // keep cache - mapping.generatedText = text; - - return text; - -}; - -const getOriginalText = (mapping, originalState) => { - - const originalText = mapping.originalText; - if (typeof originalText === 'string') { - return originalText; - } - - let text = ''; - - const { positionMapping } = originalState; - const nextMapping = findNextOriginalDiffMapping(originalState, mapping); - if (nextMapping) { - text = positionMapping.getSlice(mapping.originalOffset, nextMapping.originalOffset); - } else { - // to the end - text = positionMapping.getSlice(mapping.originalOffset); - } - - // the last two items could have same line and column - // so can NOT pre-calculate original end offset, just search new line with regex - - // never cross line - const newLineIndex = text.search(/\r?\n/); - if (newLineIndex !== -1) { - text = text.slice(0, newLineIndex); - } - - // keep cache - mapping.originalText = text; - - return text; -}; - -// ======================================================================================================== - -const getBlockStartPosition = (originalText) => { - // start block characters - - // originalText: 'argument) {', - // generatedLeft: 'o', - // generatedRight: '&&' - - // function/block could be started with {( - const startBlockIndex = originalText.search(/[<{(]/); - if (startBlockIndex !== -1) { - return startBlockIndex; - } - - // end a block - const list = ['>', '}', ')']; - for (const s of list) { - const endBlockIndex = originalText.lastIndexOf(s); - if (endBlockIndex !== -1) { - return endBlockIndex + 1; - } - } - - return -1; -}; - -const getBlockEndPosition = (originalText) => { - // generatedText: 'e),', - // generatedPos: 2, - // originalText: 'prop))' - - // generatedText: ' = false)', - // generatedPos: 8, - // originalText: '=false"' - - // generatedText: '), 1 /* TEXT */)])])) : (0,vue__...)("v-if", true)], 6 /* CLASS, STYLE */);', - // generatedPos: 17, - // originalText: ' }}' - - const list = ['>', '}', ')']; - for (const s of list) { - const endBlockIndex = originalText.lastIndexOf(s); - if (endBlockIndex !== -1) { - return endBlockIndex + 1; - } - } - - const startBlockIndex = originalText.search(/[<{(]/); - if (startBlockIndex !== -1) { - return startBlockIndex; - } - - return -1; -}; - -const getOriginalStartPosition = (originalText, generatedText, generatedPos) => { - - if (!originalText.length) { - return 0; - } - - if (originalText === generatedText) { - return generatedPos; - } - - // ============================ - // left matched - - // originalText: '1;', - // generatedText: '1;else', - // generatedLeft: '1;' - - const generatedLeft = generatedText.slice(0, generatedPos); - if (originalText.startsWith(generatedLeft)) { - return generatedLeft.length; - } - - // ============================ - // no case for right - // const generatedRight = generatedText.slice(generatedPos); - // if (originalText.endsWith(generatedRight)) { - // return originalText.length - generatedRight.length; - // } - - // ============================ - // starts with original text (few case possible useless) - - // generatedText "(void 0, void 0, void 0, function* () {" - // originalText " {" - - // generatedLen: 1629, - // generatedText: '__exports__);\r\n\r\n/***/ }),\r\n\r\n/***/ "./packages/v8', - // generatedPos: 489, - // originalText: '__exports__', - - // original less, generated more - // const includeIndex = generatedText.indexOf(originalText); - // if (includeIndex !== -1) { - - // console.log('=================== includeIndex', includeIndex, generatedPos); - // console.log(JSON.stringify(generatedText.slice(0, originalText.length + includeIndex + 10))); - // console.log(JSON.stringify(originalText)); - - // if (includeIndex >= generatedPos) { - // return 0; - // } - - // return originalText.length; - // } - - // ============================ - // {} () <> - const blockIndex = getBlockStartPosition(originalText); - if (blockIndex !== -1) { - return blockIndex; - } - - // ============================ - // end characters - - // ends with ">" in vue - // - - // originalText: '">', - // generatedText: ' ? ((0,vue__.openBlock)(), ' - - // originalMethod?.apply - // originalText: '?.' no ? - - const indexEndBlock = originalText.search(/(?<=[;,:"'\s])/); - if (indexEndBlock !== -1) { - return indexEndBlock; - } - - // console.log('===================================================================='); - // console.log('start position can NOT be fixed'); - // console.log({ - // generatedText, - // generatedPos, - // originalText - // // originalPos - // }); - - // ============================ - // can NOT be fixed - // using original column - return 0; -}; - -const getOriginalEndPosition = (originalText, generatedText, generatedPos) => { - - if (!originalText.length) { - return 0; - } - - if (originalText === generatedText) { - return generatedPos; - } - - // ============================ - // {} () <> - const blockIndex = getBlockEndPosition(originalText); - if (blockIndex !== -1) { - return blockIndex; - } - - // console.log('===================================================================='); - // console.log('end position can NOT be fixed'); - // console.log({ - // generatedText, - // generatedPos, - // originalText - // // originalPos - // }); - - // ============================ - // can NOT be fixed - // to the line end - return originalText.length; -}; - -// ======================================================================================================== - -const fixStartColumn = (startMapping, range, generatedState, originalState) => { - // exact matched no need fix - if (startMapping.exact) { - // originalColumn is the column - return; - } - - // fix column - const originalColumn = startMapping.originalColumn; - - const originalText = getOriginalText(startMapping, originalState); - const generatedText = getGeneratedText(startMapping, generatedState); - - // actual generatedOffset < range startOffset - const generatedPos = range.startOffset - startMapping.generatedOffset; - - const originalPos = getOriginalStartPosition(originalText, generatedText, generatedPos); - - // if (originalState.sourcePath.endsWith('src/components/report.vue') && range.count === 0) { - // console.log('===================================================================='); - // console.log('fix startMapping', originalState.sourcePath); - // console.log(startMapping); - // console.log({ - // generatedPos, - // originalPos - // }); - // } - - // failed if originalPos = 0 - startMapping.column = originalColumn + originalPos; - -}; - -// ======================================================================================================== - -const fixEndColumn = (endMapping, range, startMapping, generatedState, originalState) => { - - const originalColumn = endMapping.originalColumn; - - // exact matched, but already -1 so need +1 - if (endMapping.exact) { - endMapping.column = originalColumn + 1; - return; - } - - // ================================ - // diff line, to the line end - if (endMapping.originalLine !== startMapping.originalLine) { - endMapping.column = Infinity; - return; - } - - // ================================ - // in the same line - - const originalText = getOriginalText(endMapping, originalState); - - // exclusive, need exclude some end strings - if (!endMapping.exclusive) { - endMapping.column = originalColumn + originalText.length; - return; - } - - - const generatedText = getGeneratedText(endMapping, generatedState); - - // actual generatedOffset < range endOffset - const generatedPos = range.endOffset - endMapping.generatedOffset; - - const originalPos = getOriginalEndPosition(originalText, generatedText, generatedPos); - - // if (originalState.sourcePath.endsWith('v8/src/app.vue') && range.count === 0) { - // console.log('===================================================================='); - // console.log('fix endMapping', originalState.sourcePath); - // console.log(startMapping, endMapping); - // const generatedLen = generatedText.length; - // const gt = generatedLen > 50 ? generatedText.slice(0, 50) : generatedText; - // console.log({ - // generatedLen, - // generatedText: gt, - // generatedPos, - // originalText, - // originalPos - // }); - // } - - // failed if originalPos = 0 - endMapping.column = originalColumn + originalPos; - -}; - -// ======================================================================================================== - -const isOffsetCrossLine = (startMapping, offset) => { - const { exact, generatedEndOffset } = startMapping; - if (!exact && generatedEndOffset) { - if (offset >= generatedEndOffset) { - return true; - } - } - return false; -}; - -const findStartMapping = (range, generatedState, originalMap) => { - - // startOffset: inclusive - const startOffset = range.startOffset; - - const startMapping = findOffsetMapping(generatedState, startOffset); - if (!startMapping) { - return; - } - - // check end offset for start mapping only - // start mapping is inclusive, do not allow cross line - // but end mapping is exclusive and offset do -1, possible no mapping found, do not check it - if (isOffsetCrossLine(startMapping, startOffset)) { - // try next mapping if its offset in the range - const nextMapping = findNextGeneratedDiffMapping(generatedState, startMapping); - if (!nextMapping) { - return; - } - - // ignore out of range - if (nextMapping.generatedOffset >= range.endOffset) { - return; - } - - // if (startMapping.sourceIndex === 1 && range.count === 0) { - // console.log('========================================================='); - // console.log('find next start'); - // console.log(startMapping, nextMapping, range); - // } - - // exact and column - Object.assign(startMapping, nextMapping, { - exact: true, - column: nextMapping.originalColumn - }); - - } - - - // check source first, sourceIndex could be undefined - const sourceIndex = startMapping.sourceIndex; - - const originalState = originalMap.get(sourceIndex); - if (!originalState) { - return; - } - - return { - startMapping, - originalState - }; -}; - - -const findEndMapping = (range, generatedState, startMapping) => { - - // endOffset: exclusive - const endOffset = range.endOffset; - - // there could be some comments before end mapping even exact matched - const endMapping = findOffsetMapping(generatedState, endOffset - 1); - if (!endMapping) { - return; - } - - // cross file ignore - if (endMapping.sourceIndex !== startMapping.sourceIndex) { - return; - } - - // still exclusive - const exclusiveMapping = findOffsetMapping(generatedState, endOffset); - if (exclusiveMapping && exclusiveMapping.originalOffset === endMapping.originalOffset) { - endMapping.exclusive = true; - } - - return endMapping; -}; - -const findOriginalRange = (range, generatedState, originalMap) => { - - // startOffset: inclusive - // endOffset: exclusive - - const startResult = findStartMapping(range, generatedState, originalMap); - if (!startResult) { - return; - } - const { startMapping, originalState } = startResult; - - // ================================================================================== - // if (originalState.sourcePath.endsWith('app/app.module.ts')) { - // originalState.showLog = true; - // console.log('============================================================'); - // console.log(originalState.sourcePath); - // console.log(range); - // } else { - // originalState.showLog = false; - // } - // ================================================================================== - - const endMapping = findEndMapping(range, generatedState, startMapping); - if (!endMapping) { - return; - } - - // fix start - fixStartColumn(startMapping, range, generatedState, originalState); - - // fix end - fixEndColumn(endMapping, range, startMapping, generatedState, originalState); - - const positionMapping = originalState.positionMapping; - const originalStart = positionMapping.locationToOffset({ - line: startMapping.originalLine + 1, - column: startMapping.column - }); - const originalEnd = positionMapping.locationToOffset({ - line: endMapping.originalLine + 1, - column: endMapping.column - }); - - // range start greater than end - if (originalStart >= originalEnd) { - // console.log(`start >= end: ${originalState.sourcePath}`); - // console.log(range, originalRange); - return; - } - - const originalRange = { - startOffset: originalStart, - endOffset: originalEnd, - count: range.count - }; - - // if (originalState.showLog) { - // console.log('startMapping and endMapping:'); - // console.log(startMapping, endMapping); - // console.log(originalStart, originalEnd); - // } - - return { - originalRange, - originalState - }; - -}; - -module.exports = findOriginalRange; diff --git a/lib/plugins/coverage/converter/info-branch.js b/lib/plugins/coverage/converter/info-branch.js deleted file mode 100644 index 45634cd8..00000000 --- a/lib/plugins/coverage/converter/info-branch.js +++ /dev/null @@ -1,30 +0,0 @@ -module.exports = class InfoBranch { - constructor(sLoc, eLoc, count) { - this.startLine = sLoc.line; - this.startColumn = sLoc.column; - this.endLine = eLoc.line; - this.endColumn = eLoc.column; - this.count = count; - } - - generate() { - const location = { - start: { - line: this.startLine, - column: this.startColumn - }, - end: { - line: this.endLine, - column: this.endColumn - } - }; - return { - type: 'branch', - line: this.startLine, - loc: location, - locations: [{ - ... location - }] - }; - } -}; diff --git a/lib/plugins/coverage/converter/info-function.js b/lib/plugins/coverage/converter/info-function.js deleted file mode 100644 index 4716cd42..00000000 --- a/lib/plugins/coverage/converter/info-function.js +++ /dev/null @@ -1,29 +0,0 @@ -module.exports = class InfoFunction { - constructor(sLoc, eLoc, count, name) { - this.startLine = sLoc.line; - this.startColumn = sLoc.column; - this.endLine = eLoc.line; - this.endColumn = eLoc.column; - this.count = count; - this.name = name || '(anonymous)'; - } - - generate() { - const loc = { - start: { - line: this.startLine, - column: this.startColumn - }, - end: { - line: this.endLine, - column: this.endColumn - } - }; - return { - name: this.name, - decl: loc, - loc: loc, - line: this.startLine - }; - } -}; diff --git a/lib/plugins/coverage/converter/info-line.js b/lib/plugins/coverage/converter/info-line.js deleted file mode 100644 index 31981fc3..00000000 --- a/lib/plugins/coverage/converter/info-line.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = class InfoLine { - constructor(line, column, count = 1) { - this.line = line; - this.column = column; - this.count = count; - } - - generate() { - return { - start: { - line: this.line, - column: 0 - }, - end: { - line: this.line, - column: this.column - } - }; - } -}; diff --git a/lib/plugins/coverage/converter/position-mapping.js b/lib/plugins/coverage/converter/position-mapping.js deleted file mode 100644 index c4a3b03b..00000000 --- a/lib/plugins/coverage/converter/position-mapping.js +++ /dev/null @@ -1,183 +0,0 @@ - -const findLine = function(list, position) { - let start = 0; - let end = list.length - 1; - while (end - start > 1) { - const i = Math.floor((start + end) * 0.5); - const item = list[i]; - if (position < item.start) { - end = i; - continue; - } - if (position > item.end) { - start = i; - continue; - } - return list[i]; - } - // last two items, less is start - const endItem = list[end]; - if (position < endItem.start) { - return list[start]; - } - return list[end]; -}; - -// ======================================================================================= - -const isLineSingleCommented = (codeStr) => { - const singleBlock = /^\s*\/\//g; - return singleBlock.test(codeStr); -}; - -const isLineStartCommented = (codeStr) => { - const multiStartBlock = /^\s*\/\*/g; - return multiStartBlock.test(codeStr); -}; - -// multi-comment end but not at end -const isLineEndCommented = (codeStr) => { - const multiEndBlock = /.*\*\//g; - return multiEndBlock.test(codeStr); -}; - -const isLineEndCommentedToEnd = (codeStr) => { - const multiEndBlock = /.*\*\/\s*$/g; - return multiEndBlock.test(codeStr); -}; - -const isLineBlank = (codeStr) => { - const blankBlock = /\S/; - return !blankBlock.test(codeStr); -}; - -class PositionMapping { - constructor(source) { - this.source = source; - this.lines = this.buildLines(source); - this.commentedLines = []; - this.blankLines = []; - this.parseLines(this.lines); - } - - // ============================================================================= - - getSlice(s, e) { - return this.source.slice(s, e); - } - - // 1-base - locationToOffset(location) { - const { line, column } = location; - // 1-based - const lineInfo = this.lines[line - 1]; - if (lineInfo) { - if (column === Infinity) { - return lineInfo.start + lineInfo.length; - } - return lineInfo.start + column; - } - return 0; - } - - // 1-base - offsetToLocation(offset) { - const lineInfo = findLine(this.lines, offset); - - let indent = lineInfo.text.search(/\S/); - if (indent === -1) { - indent = lineInfo.length; - } - - // 1-base - const line = lineInfo.line + 1; - const column = Math.min(Math.max(offset - lineInfo.start, 0), lineInfo.length); - - return { - ... lineInfo, - line, - column, - indent - }; - } - - // 1-base - getLine(line) { - return this.lines[line - 1]; - } - - // ============================================================================= - - buildLines(content) { - let pos = 0; - const lines = content.split(/\n/).map((text, line) => { - - let n = 1; - // may ends with \r (don't consider double \r\r for now) - const reg = /\r$/; - if (reg.test(text)) { - text = text.slice(0, -1); - n += 1; - } - - const length = text.length; - const start = pos; - const end = start + length; - - pos += length + n; - - return { - line, - start, - end, - length, - text - }; - }); - - return lines; - } - - parseLines(lines) { - const commentedLines = []; - const blankLines = []; - - let startCommented = false; - - const multiEndHandler = (text, i) => { - if (isLineEndCommented(text)) { - startCommented = false; - if (isLineEndCommentedToEnd(text)) { - commentedLines.push(i); - } - return; - } - commentedLines.push(i); - }; - - lines.forEach((line, i) => { - const text = line.text; - if (startCommented) { - return multiEndHandler(text, i); - } - if (isLineStartCommented(text)) { - startCommented = true; - return multiEndHandler(text, i); - } - if (isLineSingleCommented(text)) { - commentedLines.push(i); - return; - } - if (isLineBlank(text)) { - blankLines.push(i); - } - }); - - this.commentedLines = commentedLines; - this.blankLines = blankLines; - } - -} - -module.exports = PositionMapping; - diff --git a/lib/plugins/coverage/converter/source-path.js b/lib/plugins/coverage/converter/source-path.js deleted file mode 100644 index 070f82b0..00000000 --- a/lib/plugins/coverage/converter/source-path.js +++ /dev/null @@ -1,140 +0,0 @@ -const path = require('path'); -const Util = require('../../../utils/util.js'); - -const resolveUrl = (input) => { - let url; - try { - url = new URL(input); - } catch (e) { - // console.error('error url', input); - return; - } - return url; -}; - -const filterPath = (str) => { - str = decodeURI(str); - // remove / of start, end or double, ./ ../ - const ls = str.split('/').map((it) => { - it = it.trim(); - // remove illegal characters except / - it = it.replace(/[\\:*?"<>|]/g, ''); - // space - it = it.replace(/\s+/g, '-'); - return it; - }).filter((item) => { - if (!item || item === '.' || item === '..') { - return false; - } - return true; - }); - return ls.join('/'); -}; - -const getSourcePath = (url, index = '', type = '') => { - - if (!url) { - // anonymous scripts will have __playwright_evaluation_script__ as their URL. - url = ['file://anonymous', index ? `-${index}` : '', type ? `.${type}` : ''].join(''); - } - - const u = resolveUrl(url); - if (u) { - const host = [u.hostname, u.port].filter((it) => it).join('-'); - // always start with '/' - const pathname = u.pathname; - - let p = host + pathname; - // webpack://monocart-v8/packages/v8/src/app.vue?5cc4 - if (u.search) { - p += `/${u.search}`; - } - - return filterPath(p); - } - - const relPath = Util.relativePath(url); - return filterPath(relPath); -}; - -const getSourceType = (sourcePath) => { - let ext = path.extname(sourcePath); - let type = ''; - if (ext) { - ext = ext.slice(1); - const reg = /^[a-z0-9]+$/; - if (reg.test(ext)) { - type = ext; - } - } - return type; -}; - -// ======================================================================================================== - -const initIstanbulSourcePath = (coverageData, fileSources, sourcePathHandler) => { - if (typeof sourcePathHandler !== 'function') { - return coverageData; - } - - const newCoverage = {}; - Object.keys(coverageData).forEach((sourcePath) => { - // previous coverage and source - const item = coverageData[sourcePath]; - const source = fileSources[sourcePath]; - - // new source path, second is source url - const newSourcePath = sourcePathHandler(sourcePath, ''); - if (typeof newSourcePath === 'string' && newSourcePath) { - sourcePath = newSourcePath; - } - - // update source path - item.path = sourcePath; - newCoverage[sourcePath] = item; - // update source for the new path - if (source) { - fileSources[sourcePath] = source; - } - }); - - return newCoverage; -}; - -// ======================================================================================================== - -const mergeSourceRoot = (sourceRoot, sourceName) => { - if (sourceName.startsWith(sourceRoot)) { - return sourceName; - } - return sourceRoot + sourceName; -}; - -const initSourceMapSourcePath = (sourceMap, fileUrls, sourcePathHandler) => { - // reset sourceRoot - const sourceRoot = sourceMap.sourceRoot || ''; - sourceMap.sourceRoot = ''; - - sourceMap.sources = sourceMap.sources.map((sourceName, i) => { - const sourceUrl = mergeSourceRoot(sourceRoot, sourceName); - - let sourcePath = getSourcePath(sourceUrl, i + 1); - if (typeof sourcePathHandler === 'function') { - const newSourcePath = sourcePathHandler(sourcePath, sourceUrl); - if (typeof newSourcePath === 'string' && newSourcePath) { - sourcePath = newSourcePath; - } - } - - fileUrls[sourcePath] = sourceUrl; - return sourcePath; - }); - -}; - -module.exports = { - getSourcePath, - getSourceType, - initIstanbulSourcePath, - initSourceMapSourcePath -}; diff --git a/lib/plugins/coverage/coverage.js b/lib/plugins/coverage/coverage.js index 414ccfab..01180800 100644 --- a/lib/plugins/coverage/coverage.js +++ b/lib/plugins/coverage/coverage.js @@ -1,302 +1,115 @@ const fs = require('fs'); const path = require('path'); - +const CoverageReport = require('monocart-coverage-reports'); const Util = require('../../utils/util.js'); -const { - initIstanbulData, mergeIstanbulCoverage, saveIstanbulReport -} = require('./istanbul/istanbul.js'); -const { - initV8ListAndSourcemap, mergeV8Coverage, saveV8Report -} = require('./v8/v8.js'); - -const { convertV8List } = require('./converter/converter.js'); - -const artifactsDirName = '.artifacts'; - -const defaultOptions = { - - // Defaults to test title - // title - // outputDir - // outputName - - // (Boolean) Whether to convert to Istanbul report from V8 list. Defaults to `html-spa` report | (String) report name | (Array) multiple reports - toIstanbul: false, - - // (Function) A filter function to execute for each element in the V8 list. - entryFilter: null, - - // (Function) A filter function to execute for each element in the sources which unpacked from the source map. - sourceFilter: null, - - // Istanbul Only - - // source path handler - sourcePath: null, - - // (usually not used) source finder for Istanbul HTML report - sourceFinder: null, - - // (Boolean) Whether to create `lcov.info` - lcov: false, - // (Object) Istanbul watermarks, see [here](https://github.com/istanbuljs/istanbuljs/tree/master/packages/istanbul-lib-report) - // watermarks: {}, - // (Array) V8 watermarks, Defaults to `[50, 80]` - // watermarks: [50, 80], +const attachCoverageReport = async (data, testInfo, options = {}) => { - // (Boolean) Whether inline all scripts to the single HTML file. V8 only. - inline: false + const reporterOptions = Util.resolveReporterOptions(testInfo); -}; - -// ======================================================================================================== + const outputDir = Util.resolveOutputDir(testInfo, options); + const folderName = `coverage-${Util.resolveTestIdWithRetry(testInfo)}`; -const generateV8ListReport = async (v8list, coverageData, fileSources, options) => { - const { toIstanbul, lcov } = options; - const istanbulReport = toIstanbul || lcov; - const v8Report = !toIstanbul; - - let report; - if (istanbulReport) { - report = await saveIstanbulReport(coverageData, fileSources, options); - } - if (v8Report) { - report = await saveV8Report(v8list, options); + // support multiple calls + let outputName = folderName; + let htmlDir = path.resolve(outputDir, outputName); + let i = 0; + while (fs.existsSync(htmlDir)) { + i += 1; + outputName = `${folderName}-${i}`; + htmlDir = path.resolve(outputDir, outputName); } - return report; -}; + const outputFile = path.resolve(outputDir, outputName, 'index.html'); -// ======================================================================================================== + const coverageOptions = { + name: `Coverage Report - ${testInfo.title}`, + outputFile, + assetsPath: '../assets', + logging: reporterOptions.logging, + ... options + }; -const saveReportAttachment = (testInfo, report, htmlDir) => { + const coverageReport = new CoverageReport(coverageOptions); + await coverageReport.add(data); + const report = await coverageReport.generate(); + // save report json const definition = Util.attachments.coverage; - // save report const reportPath = path.resolve(htmlDir, definition.reportFile); Util.writeJSONSync(reportPath, report); + // save attachments html link testInfo.attachments.push({ name: definition.name, contentType: definition.contentType, path: path.resolve(htmlDir, 'index.html') }); -}; - -// ======================================================================================================== - -const generateIstanbulReport = (istanbulData, testInfo, options) => { - - const { coverageData, fileSources } = initIstanbulData(istanbulData, options); - const report = saveIstanbulReport(coverageData, fileSources, options); - - saveReportAttachment(testInfo, report, options.htmlDir); return report; }; - // ======================================================================================================== -const generateV8Coverage = async (v8list, testInfo, options) => { - - // init v8list and unpack sourcemap - const inlineSourceMap = true; - v8list = await initV8ListAndSourcemap(v8list, options, inlineSourceMap); - - const { coverageData, fileSources } = await convertV8List(v8list, options); - - const report = await generateV8ListReport(v8list, coverageData, fileSources, options); - - saveReportAttachment(testInfo, report, options.htmlDir); +const getGlobalCoverageOptions = async (reporterOptions) => { - return report; -}; - -const attachCoverageReport = (data, testInfo, options = {}) => { - - const logging = Util.resolveLogging(testInfo, options); - Util.initLoggingLevel(logging, 'coverage-attach'); - - if (!data) { - Util.logError(`invalid coverage data: ${testInfo.title}`); - return; - } + const reporterOutputFile = await Util.resolveOutputFile(reporterOptions.outputFile); + const outputDir = path.dirname(reporterOutputFile); + const outputFile = path.resolve(outputDir, 'coverage', 'index.html'); - options = { - ... defaultOptions, - // default title - title: `Coverage Report - ${testInfo.title}`, - outputDir: Util.resolveOutputDir(testInfo, options), - outputName: `coverage-${Util.resolveTestIdWithRetry(testInfo)}`, - ... options + const coverageOptions = { + name: `Coverage Report - ${reporterOptions.name}`, + outputFile, + assetsPath: '../assets', + logging: reporterOptions.logging, + // merge all global coverage options + ... reporterOptions.coverage }; - // support multiple calls - let htmlDir = path.resolve(options.outputDir, options.outputName); - let i = 1; - while (fs.existsSync(htmlDir)) { - const outputName = `${options.outputName}-${i}`; - htmlDir = path.resolve(options.outputDir, outputName); - i += 1; - } - - Util.initDir(htmlDir); - options.htmlDir = htmlDir; - - const dataType = Array.isArray(data) ? 'v8' : 'istanbul'; - // console.log('data type', dataType); - - if (dataType === 'v8') { - return generateV8Coverage(data, testInfo, options); - } - - return generateIstanbulReport(data, testInfo, options); + return coverageOptions; }; -// ======================================================================================================== - // add coverage report to global, v8list only -const addCoverageReport = async (data, testInfo, options = {}) => { - - const logging = Util.resolveLogging(testInfo, options); - Util.initLoggingLevel(logging, 'coverage-add'); - - if (!data) { - Util.logError(`invalid coverage data: ${testInfo.title}`); - return; - } - - // global outputDir - const outputDir = await Util.resolveGlobalOutputDir(testInfo, options); +const addCoverageReport = async (data, testInfo) => { - const coverageOptions = Util.resolveCoverageOptions(testInfo, options); - - options = { - ... defaultOptions, - // use reporter dir as output dir, NOT test output dir - outputDir, - outputName: 'coverage', - ... coverageOptions - }; - - const reportDir = path.resolve(options.outputDir, options.outputName); - // do NOT empty dir, there are previous artifacts generated - - // artifactsDir for temp artifact files - const artifactsDir = path.resolve(reportDir, artifactsDirName); - options.artifactsDir = artifactsDir; - - const filename = `coverage-${Util.resolveTestIdWithRetry(testInfo)}.json`; - const jsonPath = path.resolve(artifactsDir, filename); - - const report = { - title: testInfo.title - }; - - if (Array.isArray(data)) { - // init v8list and unpack sourcemap - const inlineSourceMap = false; - report.data = await initV8ListAndSourcemap(data, options, inlineSourceMap); - } else { - // istanbul - report.data = data; - } - - const artifactContent = JSON.stringify({ - type: 'coverage', - data: report - }); + const reporterOptions = Util.resolveReporterOptions(testInfo); + const coverageOptions = await getGlobalCoverageOptions(reporterOptions); - await Util.writeFile(jsonPath, artifactContent); + const coverageReport = new CoverageReport(coverageOptions); + const results = await coverageReport.add(data); - const definition = Util.attachments.artifact; - testInfo.attachments.push({ - name: definition.name, - contentType: definition.contentType, - path: jsonPath - }); - - return report; + return results; }; -// ======================================================================================================== - -const getGlobalCoverageData = async (dataList, options) => { - - // get first and check v8list or istanbul data - const firstData = dataList[0].data; - const dataType = Array.isArray(firstData) ? 'v8' : 'istanbul'; - // console.log('data type', dataType); +const generateGlobalCoverageReport = async (reporterOptions) => { - // v8list - if (dataType === 'v8') { - // merge v8list first - const v8list = await mergeV8Coverage(dataList, options); - // console.log('after merge', v8list.map((it) => it.url)); - - const { coverageData, fileSources } = await convertV8List(v8list, options); - return generateV8ListReport(v8list, coverageData, fileSources, options); - - } + const coverageOptions = await getGlobalCoverageOptions(reporterOptions); - // istanbul data - const istanbulData = mergeIstanbulCoverage(dataList, options); - const { coverageData, fileSources } = initIstanbulData(istanbulData, options); - return saveIstanbulReport(coverageData, fileSources, options); + const coverageReport = new CoverageReport(coverageOptions); -}; - -// global coverage report, run different process with addCoverageReport -const addGlobalCoverageReport = async (dataList, coverageOptions) => { - - Util.logInfo('generating global coverage report ...'); - - const options = { - ... defaultOptions, - ... coverageOptions - }; - - const reportDir = path.resolve(options.outputDir, options.outputName); - - // empty dir except artifacts dir - if (fs.existsSync(reportDir)) { - fs.readdirSync(reportDir).forEach((itemName) => { - if (itemName === artifactsDirName) { - return; - } - Util.rmSync(path.resolve(reportDir, itemName)); - }); + // check if has cache dir + const cacheDir = await coverageReport.getCacheDir(); + if (!cacheDir) { + return; } - options.htmlDir = reportDir; + const report = await coverageReport.generate(); + // report props: type, htmlPath, name, watermarks, summary, files - // artifactsDir for temp artifact files - const artifactsDir = path.resolve(reportDir, artifactsDirName); - options.artifactsDir = artifactsDir; + // showing list props: global, name, path, title - const report = await getGlobalCoverageData(dataList, options); - // console.log(report); - - // ============================================================= - // remove artifacts dir after report generated - if (Util.loggingType !== 'debug') { - Util.rmSync(artifactsDir); - } - // ============================================================= + // console.log('global coverage report', report); return { global: true, - name: 'coverage', - path: report.htmlPath, - summary: report.summary, - title: options.title + type: 'coverage', + name: report.name, + path: report.htmlPath }; }; module.exports = { addCoverageReport, - addGlobalCoverageReport, - attachCoverageReport + attachCoverageReport, + generateGlobalCoverageReport }; diff --git a/lib/plugins/coverage/istanbul/istanbul-summary.js b/lib/plugins/coverage/istanbul/istanbul-summary.js deleted file mode 100644 index 02dd820b..00000000 --- a/lib/plugins/coverage/istanbul/istanbul-summary.js +++ /dev/null @@ -1,49 +0,0 @@ -const { istanbulLibReport } = require('../../../runtime/monocart-coverage.js'); - -const ReportBase = istanbulLibReport.ReportBase; -class IstanbulSummary extends ReportBase { - - onStart(root, context) { - this.context = context; - this.summary = {}; - this.files = []; - } - - addStatus(data) { - Object.keys(data).forEach((k) => { - const item = data[k]; - // low, medium, high, unknown - item.status = this.context.classForPercent(k, item.pct); - }); - } - - onSummary(node) { - if (!node.isRoot()) { - return; - } - this.summary = node.getCoverageSummary().data; - this.addStatus(this.summary); - } - - onDetail(node) { - const fileSummary = node.getCoverageSummary().data; - this.addStatus(fileSummary); - fileSummary.name = node.getRelativeName(); - this.files.push(fileSummary); - } - - onEnd() { - // console.log('onEnd'); - } - - getReport() { - return { - type: 'istanbul', - summary: this.summary, - files: this.files - }; - } -} - - -module.exports = IstanbulSummary; diff --git a/lib/plugins/coverage/istanbul/istanbul.js b/lib/plugins/coverage/istanbul/istanbul.js deleted file mode 100644 index e7fa39f5..00000000 --- a/lib/plugins/coverage/istanbul/istanbul.js +++ /dev/null @@ -1,171 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -const istanbulReports = require('istanbul-reports'); - -const Util = require('../../../utils/util.js'); -const IstanbulSummary = require('./istanbul-summary.js'); - -const { istanbulLibReport, istanbulLibCoverage } = require('../../../runtime/monocart-coverage.js'); - -const { initIstanbulSourcePath } = require('../converter/source-path.js'); - -const getIstanbulReportList = (toIstanbul) => { - if (typeof toIstanbul === 'boolean') { - return [{ - name: 'html-spa', - options: {} - }]; - } - - if (typeof toIstanbul === 'string') { - return [{ - name: toIstanbul, - options: {} - }]; - } - - if (Array.isArray(toIstanbul)) { - return toIstanbul.map((item) => { - if (typeof item === 'string') { - return { - name: item, - options: {} - }; - } - if (item && item.name) { - return item; - } - }).filter((it) => it); - } - - return [{ - name: 'html-spa', - options: {} - }]; -}; - -const saveIstanbulReport = (coverageData, fileSources, options) => { - - const coverageMap = istanbulLibCoverage.createCoverageMap(coverageData); - - const { watermarks, defaultSummarizer } = options; - const istanbulOptions = { - watermarks, - defaultSummarizer - }; - - // https://github.com/istanbuljs/istanbuljs/tree/master/packages/istanbul-lib-report - const contextOptions = { - watermarks: { - statements: [50, 80], - functions: [50, 80], - branches: [50, 80], - lines: [50, 80] - }, - // The summarizer to default to (may be overridden by some reports) - // values can be nested/flat/pkg. Defaults to 'pkg' - defaultSummarizer: 'nested', - - ... istanbulOptions, - - dir: options.htmlDir, - sourceFinder: (filePath) => { - - // console.log(`find file source: ${filePath}`); - - if (fileSources) { - const source = fileSources[filePath]; - if (source) { - return source; - } - } - - if (typeof options.sourceFinder === 'function') { - const source = options.sourceFinder(filePath); - if (source) { - return source; - } - } - - if (fs.existsSync(filePath)) { - return fs.readFileSync(filePath, 'utf8'); - } - - // console.log('Not found source file:', filePath); - - return `Not found source file: ${filePath}`; - }, - coverageMap - }; - - // create a context for report generation - const context = istanbulLibReport.createContext(contextOptions); - - // istanbul reports - let lcovCreated = false; - if (options.toIstanbul) { - const reportList = getIstanbulReportList(options.toIstanbul); - reportList.forEach((item) => { - if (item.name === 'lcovonly') { - lcovCreated = true; - } - const report = istanbulReports.create(item.name, item.options || {}); - report.execute(context); - }); - } - - // lcov - if (!lcovCreated && options.lcov) { - const lcovReport = istanbulReports.create('lcovonly', {}); - lcovReport.execute(context); - } - - const htmlPath = Util.relativePath(path.resolve(options.htmlDir, 'index.html')); - - // add watermarks and color - const coverageReport = new IstanbulSummary(); - coverageReport.execute(context); - const report = { - title: options.title, - htmlPath, - watermarks: contextOptions.watermarks, - ... coverageReport.getReport() - }; - - return report; -}; - -const mergeIstanbulCoverage = (dataList, options) => { - const coverageMap = istanbulLibCoverage.createCoverageMap(); - dataList.forEach((item) => { - coverageMap.merge(item.data); - }); - - const istanbulData = coverageMap.toJSON(); - - return istanbulData; -}; - -const initIstanbulData = (istanbulData, options) => { - - // force to istanbul, true is defaults to html-spa - if (!options.toIstanbul) { - options.toIstanbul = true; - } - - const fileSources = options.fileSources || {}; - - const coverageData = initIstanbulSourcePath(istanbulData, fileSources, options.sourcePath); - - return { - fileSources, - coverageData - }; -}; - -module.exports = { - saveIstanbulReport, - mergeIstanbulCoverage, - initIstanbulData -}; diff --git a/lib/plugins/coverage/v8/v8-summary.js b/lib/plugins/coverage/v8/v8-summary.js deleted file mode 100644 index 13e3bfe5..00000000 --- a/lib/plugins/coverage/v8/v8-summary.js +++ /dev/null @@ -1,80 +0,0 @@ -// https://playwright.dev/docs/api/class-coverage - -// url, text, ranges: [ {start, end} ] -const getCssSummary = (item) => { - - const source = item.source; - const total = source.length; - - let covered = 0; - const ranges = item.ranges; - if (ranges) { - ranges.forEach((range) => { - covered += range.end - range.start; - }); - } - - const uncovered = total - covered; - - return { - name: item.sourcePath, - type: item.type, - url: item.url, - total, - covered, - uncovered - }; - -}; - -// url, source, ranges:[{start,end, count}] -const getJsSummary = (item) => { - - const source = item.source; - const total = source.length; - - const uncoveredRanges = item.ranges.filter((range) => range.count === 0); - - let uncovered = 0; - - let endPos = 0; - uncoveredRanges.forEach((range) => { - const { start, end } = range; - - if (start > endPos) { - uncovered += end - start; - endPos = end; - return; - } - - if (end <= endPos) { - return; - } - - uncovered += end - endPos; - endPos = end; - - }); - - const covered = total - uncovered; - - return { - name: item.sourcePath, - type: item.type, - url: item.url, - total, - covered, - uncovered - }; -}; - -const getV8Summary = (item) => { - if (item.type === 'css') { - return getCssSummary(item); - } - return getJsSummary(item); -}; - -module.exports = { - getV8Summary -}; diff --git a/lib/plugins/coverage/v8/v8.js b/lib/plugins/coverage/v8/v8.js deleted file mode 100644 index 9f759259..00000000 --- a/lib/plugins/coverage/v8/v8.js +++ /dev/null @@ -1,260 +0,0 @@ -const Util = require('../../../utils/util.js'); -const { getV8Summary } = require('./v8-summary.js'); -const { dedupeRanges } = require('../converter/dedupe.js'); -const { getSourcePath } = require('../converter/source-path.js'); -const { mergeScriptCovs } = require('../../../runtime/monocart-coverage.js'); -const collectSourceMaps = require('../converter/collect-source-maps.js'); - -const initV8ListAndSourcemap = async (v8list, options, inlineSourceMap) => { - - // filter list first - const entryFilter = options.entryFilter; - if (typeof entryFilter === 'function') { - v8list = v8list.filter(entryFilter); - } - - // filter no source or text - // init type and css source from text - // keep functions - - v8list = v8list.filter((item) => { - if (typeof item.source === 'string' && item.functions) { - return true; - } - if (typeof item.text === 'string' && item.ranges) { - return true; - } - // 404 css, text will be empty - // Util.logError(`Invalid source: ${item.url}`); - // console.log(item); - }); - - - // do not change original v8list, to work for multiple APIs - - const sourcePathHandler = options.sourcePath; - - // init id, sourcePath - v8list = v8list.map((item, i) => { - - const data = { - url: item.url - }; - - if (item.functions) { - data.type = 'js'; - data.functions = item.functions; - data.source = item.source; - // useless here - // data.scriptId = item.scriptId; - } else if (item.ranges) { - data.type = 'css'; - data.ranges = item.ranges; - data.source = item.text; - } - - data.id = Util.calculateSha1(data.url + data.source); - - let sourcePath = getSourcePath(data.url, i + 1, data.type); - if (typeof sourcePathHandler === 'function') { - const newSourcePath = sourcePathHandler(sourcePath, data.url); - if (typeof newSourcePath === 'string' && newSourcePath) { - sourcePath = newSourcePath; - } - } - - data.sourcePath = sourcePath; - - return data; - }); - - // collect sourcemap first - const time_start = Date.now(); - const count = await collectSourceMaps(v8list, options, inlineSourceMap); - if (count) { - Util.logTime(`loaded ${count} sourcemaps`, time_start); - } - - return v8list; -}; - -// ======================================================================================================== - -// force to async -const mergeCssRanges = (itemList) => { - return new Promise((resolve) => { - - let concatRanges = []; - itemList.forEach((item) => { - concatRanges = concatRanges.concat(item.ranges); - }); - - // ranges: [ {start, end} ] - const ranges = dedupeRanges(concatRanges); - - resolve(ranges || []); - }); -}; - -const mergeJsFunctions = (itemList) => { - return new Promise((resolve) => { - - const res = mergeScriptCovs(itemList); - const functions = res && res.functions; - - resolve(functions || []); - }); -}; - -const mergeV8Coverage = async (dataList, options) => { - - let list = []; - dataList.forEach((d) => { - list = list.concat(d.data); - }); - - // console.log('before merge', list.map((it) => it.url)); - - // connect all functions and ranges - const itemMap = {}; - const mergeMap = {}; - list.forEach((item) => { - const { id } = item; - const prev = itemMap[id]; - if (prev) { - if (mergeMap[id]) { - mergeMap[id].push(item); - } else { - mergeMap[id] = [prev, item]; - } - } else { - itemMap[id] = item; - } - }); - - // merge functions and ranges - const mergeIds = Object.keys(mergeMap); - for (const id of mergeIds) { - const itemList = mergeMap[id]; - const item = itemMap[id]; - if (item.type === 'js') { - item.functions = await mergeJsFunctions(itemList); - } else { - item.ranges = await mergeCssRanges(itemList); - } - } - - list = Object.values(itemMap); - - // try to load coverage and source by id - for (const item of list) { - const { id } = item; - const sourcePath = Util.resolveArtifactSourcePath(options.artifactsDir, id); - const content = await Util.readFile(sourcePath); - if (content) { - const json = JSON.parse(content); - item.source = json.source; - item.sourceMap = json.sourceMap; - } else { - Util.logError(`failed to read source: ${item.url}`); - item.source = ''; - } - } - - return list; -}; - -// ============================================================ - -const saveV8HtmlReport = async (reportData, _options) => { - - const { - htmlDir, outputDir, inline - } = _options; - - const options = { - inline, - reportData, - jsFiles: ['monocart-code-viewer.js', 'monocart-formatter.js', 'monocart-common.js', 'monocart-v8.js'], - htmlDir, - htmlFile: 'index.html', - - outputDir, - reportDataFile: 'coverage-data.js', - assetsRelative: '../' - }; - - const htmlPath = await Util.saveHtmlReport(options); - - return htmlPath; -}; - -const saveV8Report = async (v8list, options) => { - options = { - - watermarks: [50, 80], - inline: false, - - ... options - }; - - const watermarks = options.watermarks; - - // init summary - v8list.forEach((entry) => { - entry.summary = getV8Summary(entry); - }); - - // calculate pct, status and summary - const summaryList = v8list.map((entry) => entry.summary); - let total = 0; - let covered = 0; - summaryList.forEach((item) => { - - total += item.total; - covered += item.covered; - - item.pct = Util.PNF(item.covered, item.total, 2); - item.status = Util.getStatus(item.pct, watermarks); - - }); - - const uncovered = total - covered; - - const pct = Util.PNF(covered, total, 2); - const status = Util.getStatus(pct, watermarks); - - const summary = { - total, - covered, - uncovered, - pct, - status - }; - - const htmlReport = { - title: options.title, - watermarks, - summary, - files: v8list - }; - - const htmlPath = await saveV8HtmlReport(htmlReport, options); - - const report = { - title: options.title, - type: 'v8', - htmlPath, - watermarks, - summary, - files: summaryList - }; - - return report; -}; - -module.exports = { - initV8ListAndSourcemap, - mergeV8Coverage, - saveV8Report -}; diff --git a/lib/plugins/network/network.js b/lib/plugins/network/network.js index 564ff546..40091af7 100644 --- a/lib/plugins/network/network.js +++ b/lib/plugins/network/network.js @@ -96,21 +96,25 @@ const getNetworkSummary = (log) => { const saveNetworkHtmlReport = async (reportData, _options) => { - const { - htmlDir, outputDir, inline - } = _options; + const { htmlDir, inline } = _options; + + // 'turbogrid' is in common + const jsFiles = ['monocart-code-viewer', 'monocart-formatter'].map((it) => { + return path.resolve(`node_modules/${it}/dist/${it}.js`); + }); + + jsFiles.push(path.resolve(__dirname, '../../packages/monocart-common.js')); + jsFiles.push(path.resolve(__dirname, '../../packages/monocart-network.js')); const options = { inline, reportData, - jsFiles: ['monocart-code-viewer.js', 'monocart-formatter.js', 'monocart-common.js', 'monocart-network.js'], - htmlDir, + jsFiles, + assetsPath: '../assets', + outputDir: htmlDir, htmlFile: 'index.html', - outputDir, - reportDataFile: 'network-data.js', - assetsName: 'assets', - assetsRelative: '../' + reportDataFile: 'network-data.js' }; const htmlPath = await Util.saveHtmlReport(options); diff --git a/lib/plugins/state/client.js b/lib/plugins/state/client.js index 0ba03c05..3f6dd723 100644 --- a/lib/plugins/state/client.js +++ b/lib/plugins/state/client.js @@ -1,4 +1,4 @@ -const { WebSocket } = require('../../runtime/monocart-vendor.js'); +const { WebSocket } = require('../../packages/monocart-vendor.js'); const Util = require('../../utils/util.js'); const getServerUrl = (options = {}) => { diff --git a/lib/plugins/state/state.js b/lib/plugins/state/state.js index 350eca0c..f81732e0 100644 --- a/lib/plugins/state/state.js +++ b/lib/plugins/state/state.js @@ -1,5 +1,5 @@ const EC = require('eight-colors'); -const { WebSocketServer } = require('../../runtime/monocart-vendor.js'); +const { WebSocketServer } = require('../../packages/monocart-vendor.js'); const Util = require('../../utils/util.js'); const { getServerUrl, Client } = require('./client.js'); diff --git a/lib/utils/decode-mappings.js b/lib/utils/decode-mappings.js deleted file mode 100644 index 0cfe8a44..00000000 --- a/lib/utils/decode-mappings.js +++ /dev/null @@ -1,49 +0,0 @@ -const { - Worker, isMainThread, parentPort -} = require('worker_threads'); -const Util = require('./util.js'); - -if (isMainThread) { - - const decodeMappings = (mappings = '') => { - if (typeof text !== 'string') { - mappings = String(mappings); - } - - return new Promise((resolve) => { - - const worker = new Worker(__filename); - - worker.on('message', (message) => { - if (message === 'workerReady') { - worker.postMessage(mappings); - return; - } - resolve(message); - worker.terminate(); - }); - - worker.on('error', (e) => { - Util.logError(e.message); - resolve([]); - worker.terminate(); - }); - - }); - }; - - module.exports = decodeMappings; - -} else { - - const { decode } = require('../runtime/monocart-coverage.js'); - - parentPort.on('message', (message) => { - const result = decode(message); - parentPort.postMessage(result); - }); - - parentPort.postMessage('workerReady'); - -} - diff --git a/lib/utils/parse-source.js b/lib/utils/parse-source.js index 684762ac..b3b05925 100644 --- a/lib/utils/parse-source.js +++ b/lib/utils/parse-source.js @@ -1,5 +1,5 @@ const Util = require('./util.js'); -const { parse } = require('../runtime/monocart-vendor.js'); +const { parse } = require('../packages/monocart-vendor.js'); const parseSource = (data) => { const { diff --git a/lib/utils/util.js b/lib/utils/util.js index b1eb3d18..4d13fb7f 100644 --- a/lib/utils/util.js +++ b/lib/utils/util.js @@ -8,6 +8,8 @@ const CG = require('console-grid'); const Share = require('../platform/share.js'); const { deflateSync } = require('lz-utils'); +const defaultOptions = require('../default/options.js'); + const assetsName = 'assets'; const Util = { @@ -79,24 +81,16 @@ const Util = { return path.resolve(testInfo.outputDir, '../'); }, - resolveGlobalOutputDir: async (testInfo, options) => { - if (options && options.outputDir) { - return options.outputDir; - } - const reporterOptions = Util.resolveReporterOptions(testInfo); - const outputFile = await Util.resolveOutputFile(reporterOptions.outputFile); - const outputDir = path.dirname(outputFile); - return outputDir; - }, - resolveOutputFile: async (outputFile) => { + // function to string first if (typeof outputFile === 'function') { outputFile = await outputFile(); } - if (typeof outputFile === 'string' && outputFile) { - return outputFile; + // then check string + if (!outputFile || typeof outputFile !== 'string') { + outputFile = defaultOptions.outputFile; } - return './test-results/report.html'; + return path.resolve(outputFile); }, resolveLogging: (testInfo, options) => { @@ -107,14 +101,6 @@ const Util = { return reporterOptions.logging; }, - resolveCoverageOptions: (testInfo, options) => { - const reporterOptions = Util.resolveReporterOptions(testInfo); - return { - ... reporterOptions.coverage, - ... options - }; - }, - resolveReporterOptions: (testInfo) => { if (!testInfo) { return {}; @@ -171,12 +157,11 @@ const Util = { inline, reportData, jsFiles, - htmlDir, + assetsPath, + outputDir, htmlFile, - outputDir, - reportDataFile, - assetsRelative + reportDataFile } = options; // report data @@ -186,10 +171,9 @@ const Util = { // js libs const jsList = []; for (const jsFile of jsFiles) { - const jsPath = path.resolve(__dirname, `../runtime/${jsFile}`); - const jsStr = await Util.readFile(jsPath); + const jsStr = await Util.readFile(jsFile); jsList.push({ - file: jsFile, + filename: path.basename(jsFile), str: jsStr }); } @@ -206,10 +190,13 @@ const Util = { ].join(EOL); } else { - await Util.writeFile(path.resolve(htmlDir, reportDataFile), reportDataStr); + await Util.writeFile(path.resolve(outputDir, reportDataFile), reportDataStr); + + const assetsDir = path.resolve(outputDir, assetsPath); + const relAssetsDir = Util.relativePath(assetsDir, outputDir); for (const item of jsList) { - const filePath = path.resolve(outputDir, assetsName, item.file); + const filePath = path.resolve(assetsDir, item.filename); if (!fs.existsSync(filePath)) { await Util.writeFile(filePath, item.str); } @@ -217,12 +204,12 @@ const Util = { htmlStr = [ ``, - ... jsList.map((it) => ``) + ... jsList.map((it) => ``) ].join(EOL); } // html - const htmlPath = path.resolve(htmlDir, htmlFile); + const htmlPath = path.resolve(outputDir, htmlFile); const template = Util.getTemplate(path.resolve(__dirname, '../default/template.html')); const html = Util.replace(template, { title: reportData.title, @@ -319,7 +306,7 @@ const Util = { readFile: async (filePath) => { if (fs.existsSync(filePath)) { const buf = await readFile(filePath).catch((e) => { - console.log(e); + Util.logError(`read file: ${filePath} ${e.message || e}`); }); if (Buffer.isBuffer(buf)) { return buf.toString('utf8'); @@ -350,7 +337,7 @@ const Util = { } } await writeFile(filePath, content).catch((e) => { - console.log(e); + Util.logError(`write file: ${filePath} ${e.message || e}`); }); }, @@ -433,14 +420,14 @@ const Util = { if (Util.loggingLevel < Util.loggingLevels.error) { return; } - EC.logRed(`[MCR] ${message}`); + EC.logRed(`[MR] ${message}`); }, logInfo: (message) => { if (Util.loggingLevel < Util.loggingLevels.info) { return; } - console.log(`[MCR] ${message}`); + console.log(`[MR] ${message}`); }, // grid is info level @@ -455,7 +442,7 @@ const Util = { if (Util.loggingLevel < Util.loggingLevels.debug) { return; } - console.log(`[MCR] ${message}`); + console.log(`[MR] ${message}`); }, // time is debug level @@ -465,7 +452,7 @@ const Util = { } const duration = Date.now() - time_start; const durationH = Util.TSF(duration); - const ls = [`[MCR] ${message}`, ' (']; + const ls = [`[MR] ${message}`, ' (']; if (duration <= 100) { ls.push(EC.green(durationH)); } else if (duration < 500) { diff --git a/lib/visitor.js b/lib/visitor.js index 095f24a2..4a377aa9 100644 --- a/lib/visitor.js +++ b/lib/visitor.js @@ -3,7 +3,7 @@ const path = require('path'); const EC = require('eight-colors'); const { StackUtils, codeFrameColumns, sanitize -} = require('./runtime/monocart-vendor.js'); +} = require('./packages/monocart-vendor.js'); const Util = require('./utils/util.js'); const commentsPlugin = require('./plugins/comments.js'); const defaultColumns = require('./default/columns.js'); @@ -53,7 +53,6 @@ class Visitor { this.rows = []; this.jobs = []; this.artifacts = []; - this.artifactDataMap = {}; await this.visit(this.root, this.rows); @@ -500,8 +499,6 @@ class Visitor { const list = []; - const artifact = Util.attachments.artifact; - attachments.forEach((item, i) => { if (item.body) { @@ -516,11 +513,6 @@ class Visitor { return; } - if (item.name === artifact.name && item.contentType === artifact.contentType) { - this.artifactHandler(item.path); - return; - } - // before path change this.reportHandler(item, 'audit', title); this.reportHandler(item, 'coverage', title); @@ -564,6 +556,8 @@ class Visitor { return; } + // itemName = item.name = definition.name + const jsonPath = path.resolve(path.dirname(item.path), definition.reportFile); if (!fs.existsSync(jsonPath)) { return; @@ -575,9 +569,9 @@ class Visitor { } this.artifacts.push({ - name: item.name, - path: Util.relativePath(item.path), - title: report.title || title + type: itemName, + name: report.name || title, + path: Util.relativePath(item.path) }); item.report = report; @@ -653,20 +647,6 @@ class Visitor { // ============================================================================================== - artifactHandler(p) { - const json = Util.readJSONSync(p); - if (json) { - const artifactList = this.artifactDataMap[json.type]; - if (artifactList) { - artifactList.push(json.data); - } else { - this.artifactDataMap[json.type] = [json.data]; - } - } - } - - // ============================================================================================== - caseErrorsHandler(caseItem) { const errors = caseItem.errors; diff --git a/package.json b/package.json index 06ac3622..4fa6d000 100644 --- a/package.json +++ b/package.json @@ -48,19 +48,20 @@ "koa": "^2.14.2", "koa-static-resolver": "^1.0.4", "lz-utils": "^2.0.1", + "monocart-coverage-reports": "^1.0.0", "nodemailer": "^6.9.7", "open": "^9.1.0" }, "devDependencies": { - "@playwright/test": "^1.39.0", - "axios": "^1.6.1", + "@playwright/test": "^1.40.1", + "axios": "^1.6.2", "dotenv": "^16.3.1", - "eslint": "^8.53.0", + "eslint": "^8.55.0", "eslint-config-plus": "^1.0.6", "eslint-plugin-html": "^7.1.0", - "eslint-plugin-vue": "^9.18.1", + "eslint-plugin-vue": "^9.19.2", "stylelint": "^15.11.0", "stylelint-config-plus": "^1.0.4", "vine-ui": "^3.1.12" } -} +} \ No newline at end of file diff --git a/packages/common/package.json b/packages/common/package.json index 54418c01..51199b80 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -11,6 +11,6 @@ "async-tick": "^1.0.0", "file-saver": "^2.0.5", "nice-ticks": "^1.0.2", - "turbogrid": "^3.0.9" + "turbogrid": "^3.0.10" } } diff --git a/packages/coverage/README.md b/packages/coverage/README.md deleted file mode 100644 index 084895e8..00000000 --- a/packages/coverage/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# monocart-coverage - -## Getting Started diff --git a/packages/coverage/package.json b/packages/coverage/package.json deleted file mode 100644 index 499a72a0..00000000 --- a/packages/coverage/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "monocart-coverage", - "version": "1.7.13", - "private": true, - "main": "dist/monocart-coverage.js", - "files": [ - "dist" - ], - "license": "MIT", - "dependencies": {}, - "devDependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jridgewell/sourcemap-codec": "^1.4.15", - "convert-source-map": "^2.0.0", - "istanbul-lib-coverage": "^3.2.2", - "istanbul-lib-report": "^3.0.1" - }, - "platform": "node" -} diff --git a/packages/coverage/src/index.js b/packages/coverage/src/index.js deleted file mode 100644 index b845ffa4..00000000 --- a/packages/coverage/src/index.js +++ /dev/null @@ -1,22 +0,0 @@ -import istanbulLibCoverage from 'istanbul-lib-coverage'; -import istanbulLibReport from 'istanbul-lib-report'; - -import * as convertSourceMap from 'convert-source-map'; -import axios from 'axios'; - -import { decode } from '@jridgewell/sourcemap-codec'; - -import { mergeScriptCovs } from '@bcoe/v8-coverage'; - -export { - - istanbulLibCoverage, - istanbulLibReport, - - convertSourceMap, - axios, - - decode, - - mergeScriptCovs -}; diff --git a/packages/network/package.json b/packages/network/package.json index c9a58f6b..159dd970 100644 --- a/packages/network/package.json +++ b/packages/network/package.json @@ -7,9 +7,9 @@ ], "license": "MIT", "dependencies": { - "monocart-code-viewer": "1.0.7", + "monocart-code-viewer": "1.0.8", "monocart-common": "~1.7.13", - "monocart-formatter": "1.0.6" + "monocart-formatter": "1.0.7" }, "devDependencies": {} -} +} \ No newline at end of file diff --git a/packages/network/public/index.html b/packages/network/public/index.html index 22b309e4..1fa411ed 100644 --- a/packages/network/public/index.html +++ b/packages/network/public/index.html @@ -10,7 +10,6 @@ - diff --git a/packages/reporter/package.json b/packages/reporter/package.json index 9888d8ae..5b666c99 100644 --- a/packages/reporter/package.json +++ b/packages/reporter/package.json @@ -14,7 +14,7 @@ "devDependencies": { "ansi-to-html": "^0.7.2", "github-markdown-css": "^5.4.0", - "marked": "^10.0.0" + "marked": "^11.0.0" }, "link": false } diff --git a/packages/reporter/src/components/detail/detail.vue b/packages/reporter/src/components/detail/detail.vue index 5bf8267e..815aca7d 100644 --- a/packages/reporter/src/components/detail/detail.vue +++ b/packages/reporter/src/components/detail/detail.vue @@ -688,8 +688,7 @@ const onFocus = (e) => { width: 100%; height: 100%; padding: 0 0 5px 5px; - overflow-x: hidden; - overflow-y: auto; + overflow: hidden auto; } .mcr-detail-item { diff --git a/packages/reporter/src/components/report/report.vue b/packages/reporter/src/components/report/report.vue index f020e5b6..8c06a1ad 100644 --- a/packages/reporter/src/components/report/report.vue +++ b/packages/reporter/src/components/report/report.vue @@ -223,6 +223,8 @@ const artifactsHandler = () => { // console.log(artifacts); + // props: type/name/path + // group artifacts const globalGroup = { name: 'global', @@ -235,12 +237,12 @@ const artifactsHandler = () => { globalGroup.list.push(item); return; } - const group = groups[item.name]; + const group = groups[item.type]; if (group) { group.list.push(item); } else { - groups[item.name] = shallowReactive({ - name: item.name, + groups[item.type] = shallowReactive({ + name: item.type, list: [item] }); } @@ -682,7 +684,7 @@ onActivated(() => { {{ item.title }} + >{{ item.name }} - - - - - - - monocart-v8 - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/v8/src/app.vue b/packages/v8/src/app.vue deleted file mode 100644 index d988e416..00000000 --- a/packages/v8/src/app.vue +++ /dev/null @@ -1,1036 +0,0 @@ - - - - - diff --git a/packages/v8/src/components/flyover.vue b/packages/v8/src/components/flyover.vue deleted file mode 100644 index ec34f84b..00000000 --- a/packages/v8/src/components/flyover.vue +++ /dev/null @@ -1,93 +0,0 @@ - - - - - diff --git a/packages/v8/src/components/icon-label.vue b/packages/v8/src/components/icon-label.vue deleted file mode 100644 index 0accb1af..00000000 --- a/packages/v8/src/components/icon-label.vue +++ /dev/null @@ -1,156 +0,0 @@ - - - - - diff --git a/packages/v8/src/components/report.vue b/packages/v8/src/components/report.vue deleted file mode 100644 index 7bcd80f3..00000000 --- a/packages/v8/src/components/report.vue +++ /dev/null @@ -1,462 +0,0 @@ - - - - - diff --git a/packages/v8/src/images/icons/arrow-right.svg b/packages/v8/src/images/icons/arrow-right.svg deleted file mode 100644 index 30998c0e..00000000 --- a/packages/v8/src/images/icons/arrow-right.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/packages/v8/src/images/icons/close.svg b/packages/v8/src/images/icons/close.svg deleted file mode 100644 index 4aa86c7d..00000000 --- a/packages/v8/src/images/icons/close.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/packages/v8/src/images/icons/search.svg b/packages/v8/src/images/icons/search.svg deleted file mode 100644 index fda426b8..00000000 --- a/packages/v8/src/images/icons/search.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/packages/v8/src/index.js b/packages/v8/src/index.js deleted file mode 100644 index c7082202..00000000 --- a/packages/v8/src/index.js +++ /dev/null @@ -1,4 +0,0 @@ -import { createApp } from 'vue'; -import App from './app.vue'; -const app = createApp(App); -app.mount('body'); diff --git a/packages/v8/src/utils/coverage.js b/packages/v8/src/utils/coverage.js deleted file mode 100644 index 8433d61c..00000000 --- a/packages/v8/src/utils/coverage.js +++ /dev/null @@ -1,268 +0,0 @@ -import { Mapping } from 'monocart-formatter'; -class CoverageParser { - - constructor(item, formattedContent, formattedMapping) { - - this.uncoveredLines = {}; - this.uncoveredPieces = {}; - this.executionCounts = {}; - - // parse commented and blank lines, include vue html for now - const parseLines = true; - const mapping = new Mapping(formattedContent, formattedMapping, parseLines); - this.mapping = mapping; - - const formattedLines = mapping.formattedLines; - const commentedLines = mapping.commentedLines; - const blankLines = mapping.blankLines; - - commentedLines.forEach((lineIndex) => { - this.uncoveredLines[lineIndex] = 'comment'; - }); - - blankLines.forEach((lineIndex) => { - this.uncoveredLines[lineIndex] = 'blank'; - }); - - if (item.type === 'css') { - this.parseCss(item.ranges, item.source.length); - } else { - this.parseJs(item.ranges); - } - - // remove all covered lines - const uncoveredLines = {}; - Object.keys(this.uncoveredLines).forEach((line) => { - const v = this.uncoveredLines[line]; - if (v === 'covered') { - return; - } - uncoveredLines[line] = v; - }); - - // console.log(uncoveredLines); - - this.uncoveredLines = uncoveredLines; - - this.coverage = { - totalLines: formattedLines.length, - commentedLines: commentedLines.length, - blankLines: blankLines.length, - codeLines: formattedLines.length - commentedLines.length - blankLines.length, - uncoveredLines: this.uncoveredLines, - uncoveredPieces: this.uncoveredPieces, - executionCounts: this.executionCounts, - sourcePath: item.sourcePath - }; - - } - - // ==================================================================================================== - - getUncoveredFromCovered(ranges, contentLength) { - const uncoveredRanges = []; - if (!ranges.length) { - - // nothing covered - uncoveredRanges.push({ - start: 0, - end: contentLength - }); - - return uncoveredRanges; - } - - ranges.sort((a, b) => a.start - b.start); - - let pos = 0; - ranges.forEach((range) => { - if (range.start > pos) { - uncoveredRanges.push({ - start: pos, - end: range.start - }); - } - pos = range.end; - }); - - if (pos < contentLength) { - uncoveredRanges.push({ - start: pos, - end: contentLength - }); - } - - return uncoveredRanges; - } - - // css, ranges: [ {start, end} ] - parseCss(ranges, contentLength) { - const uncoveredRanges = this.getUncoveredFromCovered(ranges, contentLength); - uncoveredRanges.forEach((range) => { - const { start, end } = range; - this.setRangeLines(start, end); - }); - - } - - // js, source, ranges: [ {start, end, count} ] - parseJs(ranges) { - - // no ranges mark all as covered - if (!ranges.length) { - return; - } - - ranges.forEach((range) => { - const { - start, end, count - } = range; - if (count > 0) { - if (count > 1) { - this.setExecutionCounts(start, end, count); - } - } else { - // set uncovered first - this.setRangeLines(start, end); - } - }); - - } - - // ==================================================================================================== - - setUncoveredLine(line, value) { - const prev = this.uncoveredLines[line]; - if (prev) { - return prev; - } - this.uncoveredLines[line] = value; - } - - setUncoveredPieces(line, value) { - - // console.log('setUncoveredPieces', line, value); - const prevList = this.uncoveredPieces[line]; - if (prevList) { - prevList.push(value); - return; - } - this.uncoveredPieces[line] = [value]; - } - - setSingleLine(sLoc, eLoc) { - // console.log(sLoc, eLoc); - - // nothing between - if (sLoc.column >= eLoc.column) { - return; - } - - // console.log(sLoc, codeOffset, eLoc); - - if (sLoc.column === sLoc.indent && eLoc.column === eLoc.length) { - // console.log('single', sLoc.line); - this.setUncoveredLine(sLoc.line, 'uncovered'); - return; - } - - // should be multiple partials in a line, like minified js - this.setUncoveredLine(sLoc.line, 'partial'); - - // set pieces for partial, only js - this.setUncoveredPieces(sLoc.line, { - start: sLoc.column, - end: eLoc.column - }); - - } - - setMultipleLines(sLoc, eLoc) { - - const firstELoc = { - ... sLoc, - column: sLoc.length - }; - this.setSingleLine(sLoc, firstELoc); - - for (let i = sLoc.line + 1; i < eLoc.line; i++) { - this.setUncoveredLine(i, 'uncovered'); - } - - const lastSLoc = { - ... eLoc, - column: eLoc.indent - }; - this.setSingleLine(lastSLoc, eLoc); - - } - - // ==================================================================================================== - - setRangeLines(start, end) { - - // console.log('setRangeLines', start, end); - - const mapping = this.mapping; - const skipIndent = true; - const sLoc = mapping.getFormattedLocation(start, skipIndent); - const eLoc = mapping.getFormattedLocation(end, skipIndent); - - if (eLoc.line === sLoc.line) { - this.setSingleLine(sLoc, eLoc); - return; - } - - this.setMultipleLines(sLoc, eLoc); - } - - // ==================================================================================================== - - // only for js - setExecutionCounts(start, end, count) { - - const mapping = this.mapping; - - const skipIndent = true; - const sLoc = mapping.getFormattedLocation(start, skipIndent); - const line = sLoc.line; - let column = sLoc.column; - - // It should never be possible to start with } - const pos = sLoc.start + column; - const char = mapping.getFormattedSlice(pos, pos + 1); - if (char === '}') { - // console.log(line, char); - column += 1; - } - - const eLoc = mapping.getFormattedLocation(end, skipIndent); - let endPos = eLoc.start; - if (eLoc.column > eLoc.indent) { - endPos += eLoc.column; - } - - // console.log(start, end, sLoc); - - const execution = { - // for start position - column, - value: count, - // for end position - end: endPos - }; - - const prevList = this.executionCounts[line]; - if (prevList) { - prevList.push(execution); - return; - } - this.executionCounts[line] = [execution]; - } -} - -export const getCoverage = (item, formattedContent, formattedMapping) => { - const parser = new CoverageParser(item, formattedContent, formattedMapping); - return parser.coverage; -}; - diff --git a/packages/v8/src/utils/util.js b/packages/v8/src/utils/util.js deleted file mode 100644 index a1f03f99..00000000 --- a/packages/v8/src/utils/util.js +++ /dev/null @@ -1,29 +0,0 @@ -import { CommonUtil } from 'monocart-common'; - -const Util = { - ... CommonUtil, - - getSourceName: (sourcePath = '') => { - const pathList = sourcePath.split('/'); - const lastName = pathList.pop(); - const dir = pathList.pop(); - - // with extname - const index = lastName.lastIndexOf('.'); - if (index !== -1) { - const ext = lastName.slice(index + 1); - const reg = /^[a-z0-9]+$/; - if (reg.test(ext)) { - return lastName; - } - } - - // with parent dir - if (dir) { - return `${dir}/${lastName}`; - } - return lastName; - } -}; - -export default Util; diff --git a/packages/vendor/package.json b/packages/vendor/package.json index adf3af14..ca218cf4 100644 --- a/packages/vendor/package.json +++ b/packages/vendor/package.json @@ -9,8 +9,8 @@ "license": "MIT", "dependencies": {}, "devDependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.23.3", + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.23.5", "sanitize-filename": "^1.6.3", "stack-utils": "^2.0.6", "ws": "^8.14.2" diff --git a/scripts/conf.cli.js b/scripts/conf.cli.js index b366b8c6..1981049a 100644 --- a/scripts/conf.cli.js +++ b/scripts/conf.cli.js @@ -79,29 +79,6 @@ const beforeReporter = (item, Util) => { return 0; }; -const beforeV8 = (item, Util) => { - - const EC = require('eight-colors'); - - // using global coverage data - const dataFile = 'coverage-data.js'; - const jsDataPath = path.resolve(__dirname, `../.temp/monocart/coverage/${dataFile}`); - if (!fs.existsSync(jsDataPath)) { - EC.logRed(`ERROR: Not found: ${jsDataPath}`); - return 0; - } - - const jsPath = path.resolve(item.buildPath, dataFile); - fs.copyFileSync(jsDataPath, jsPath); - EC.logGreen(`coverage data file copied: ${dataFile}`); - - if (!item.dependencies.files.includes(jsPath)) { - item.dependencies.files.unshift(jsPath); - } - - return 0; -}; - const beforeNetwork = (item, Util) => { const EC = require('eight-colors'); @@ -133,7 +110,7 @@ module.exports = { build: { - vendors: ['common', 'reporter', 'v8', 'network'], + vendors: ['common', 'reporter', 'network'], before: (item, Util) => { @@ -145,10 +122,6 @@ module.exports = { return beforeReporter(item, Util); } - if (item.name === 'v8') { - return beforeV8(item, Util); - } - if (item.name === 'network') { return beforeNetwork(item, Util); } @@ -165,7 +138,7 @@ module.exports = { const EC = require('eight-colors'); - const toPath = path.resolve(__dirname, '../lib/runtime'); + const toPath = path.resolve(__dirname, '../lib/packages'); // only clean if build all const totalComponents = fs.readdirSync(path.resolve(__dirname, '../packages')); @@ -196,21 +169,6 @@ module.exports = { distList.push(distPath); }); - distList.push(''); - - EC.log('get common dependencies dist files ...'); - // copy common components - const moduleList = ['monocart-code-viewer', 'monocart-formatter']; - moduleList.forEach((moduleName) => { - const distPath = path.resolve(__dirname, `../node_modules/${moduleName}/dist/${moduleName}.js`); - if (!fs.existsSync(distPath)) { - EC.logRed(`ERROR: Not found dist: ${distPath}`); - code = 1; - return; - } - distList.push(distPath); - }); - if (code) { return code; } diff --git a/tests/playwright.config.js b/tests/playwright.config.js index 81514963..6e2df514 100644 --- a/tests/playwright.config.js +++ b/tests/playwright.config.js @@ -91,6 +91,8 @@ module.exports = { timezoneOffset: 0, + // inline: false, + // global coverage coverage: { @@ -148,7 +150,7 @@ module.exports = { trend: () => { return new Promise((resolve) => { - const { axios } = require('../lib/runtime/monocart-coverage.js'); + const axios = require('axios'); axios.get('https://cenfun.github.io/monocart-reporter/index.json').then((res) => { const json = res.data; // mock data diff --git a/tests/report-coverage/report-coverage.spec.js b/tests/report-coverage/report-coverage.spec.js index 1fdb730b..b7b073cc 100644 --- a/tests/report-coverage/report-coverage.spec.js +++ b/tests/report-coverage/report-coverage.spec.js @@ -30,12 +30,12 @@ test('Take Istanbul coverage report', async ({ page }) => { // default is html-spa // toIstanbul: 'html', sourcePath: (sourcePath, fileSources) => { - console.log(sourcePath); + console.log('sourcePath', sourcePath); // replace local windows \ to server / sourcePath = sourcePath.replace(/\\/g, '/'); const filename = path.basename(sourcePath); const redirectPath = path.resolve(__dirname, '../../scripts/mock/coverage/src', filename); - console.log(redirectPath); + console.log('redirectPath', redirectPath); return redirectPath; } });