From 25036d359789637b39ccdb021a156f41237c9ffa Mon Sep 17 00:00:00 2001 From: Ben Moroze Date: Fri, 15 Jul 2022 13:54:04 -0400 Subject: [PATCH] Release 13.0.0 --- README.md | 4 +- RELEASENOTES.md | 8 + THIRDPARTYLICENSE.txt | 2 +- common/component.js | 5 +- common/index.js | 8 +- common/template/index.js | 4 +- common/template/npm.js | 42 +- config.js | 2 +- generators/add-pcss-theme/index.js | 19 +- generators/add-theme/index.js | 10 +- generators/app/templates/common/package.json | 6 +- .../hybrid/templates/common/package.json | 6 +- hybrid/index.js | 2 +- lib/scopes/app.js | 2 + lib/util/utils.js | 7 +- package.json | 10 +- template/component/js/component.json | 2 +- template/component/ts/component.json | 2 +- .../tsx/@component@-functional-template.tsx | 30 +- template/component/tsx/@component@.tsx | 6 +- template/component/tsx/README.md | 2 +- template/resource-component/component.json | 2 +- test/apiTest.js | 24 +- test/componentTest.js | 1418 +++++++++++++---- test/hybridTest.js | 16 +- test/ojcTest.js | 106 ++ test/templates/webTsApiTest/package.json | 8 +- .../src/css/demo-alta-site-min.css | 2 +- test/templates/webTsApiTest/src/index.html | 2 +- test/templates/webTsApiTest/src/js/main.js | 8 +- .../webTsApiTest/src/js/path_mapping.json | 214 +-- .../my-pack/resources/component.json | 2 +- test/templates/webTsApiTest/tsconfig.json | 8 +- test/templates/webpackLegacyTest/package.json | 10 +- .../webpackLegacyTest/path_mapping.json | 45 +- test/templates/webpackLegacyTest/src/main.js | 8 +- .../templates/webpackLegacyTest/tsconfig.json | 6 +- test/themeTest.js | 66 +- test/util/index.js | 19 +- test/vdomTest.js | 14 + test/webTest.js | 1 - test/webpackTest.js | 225 ++- 42 files changed, 1795 insertions(+), 588 deletions(-) create mode 100644 test/ojcTest.js diff --git a/README.md b/README.md index c7f4668..04b43b3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# @oracle/ojet-cli 12.1.0 +# @oracle/ojet-cli 13.0.0 ## About the module This module contains a command line interface for Oracle JET web and hybrid mobile application development. @@ -64,7 +64,7 @@ Or view help on adding a plugin: ojet help add plugin ``` -For more information on the Oracle JET CLI, refer to the [Oracle JET Developers Guide](http://www.oracle.com/pls/topic/lookup?ctx=jet1210&id=homepage). +For more information on the Oracle JET CLI, refer to the [Oracle JET Developers Guide](http://www.oracle.com/pls/topic/lookup?ctx=jet1300&id=homepage). ## [Contributing](https://github.com/oracle/ojet-cli/blob/master/CONTRIBUTING.md) Oracle JET is an open source project. Pull Requests are currently not being accepted. See [CONTRIBUTING](https://github.com/oracle/ojet-cli/blob/master/CONTRIBUTING.md) for details. diff --git a/RELEASENOTES.md b/RELEASENOTES.md index df4930f..585e1a8 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,5 +1,13 @@ ## Release Notes for ojet-cli ## +### 13.0.0 + +* Metadata to support API documentation is now emitted for vcomponents during build +* New webpack applicatons support non-vdom JavaScript and Typescript source code +* Updated default typescript version to 4.6.4 +* Add synonym for --vcomponent to allow 'functional' in addition to 'function' +* Enhancements to ease monorepo development + ### 12.1.0 * Bug fixes diff --git a/THIRDPARTYLICENSE.txt b/THIRDPARTYLICENSE.txt index 5ea273e..1107cf1 100644 --- a/THIRDPARTYLICENSE.txt +++ b/THIRDPARTYLICENSE.txt @@ -14,7 +14,7 @@ THIRD-PARTY COMPONENT LICENSE node_modules/minimist/ MIT node_modules/adm-zip/ MIT node_modules/fs-extra/ MIT -node_modules/xmldom/ MIT +node_modules/@xmldom/xmldom/ MIT node_modules/inquirer MIT Copyright (c) 2015, Google diff --git a/common/component.js b/common/component.js index 423f3d1..7486de2 100644 --- a/common/component.js +++ b/common/component.js @@ -136,7 +136,8 @@ module.exports = { * @param {object} generator object with build options */ getComponentDestPath: (generator) => { - _getComponentDestPath(generator); + const destPath = _getComponentDestPath(generator); + return destPath; } }; @@ -511,7 +512,7 @@ function _filterTsxTemplates(generator, destPath) { const componentName = _getComponentName(generator); const pathToClassBasedTemplate = path.join(destPath, `${componentName}.tsx`); const pathToFunctionalBasedTemplate = path.join(destPath, `${componentName}-functional-template.tsx`); - if (generator.options.vcomponent === 'function') { + if (generator.options.vcomponent === 'function' || generator.options.vcomponent === 'functional') { // .tsx now has functional based template after overwriting it // with .tsx contents. We need to do this because, // once the chosen template is vcomponent, then we need to use the functional diff --git a/common/index.js b/common/index.js index 47188b0..42f93ab 100644 --- a/common/index.js +++ b/common/index.js @@ -230,16 +230,20 @@ function _customizeVDOMTemplateTsconfigForWebpack() { const oraclejetConfigJson = fs.readJSONSync(pathToOraclejetConfigJson); const pathToTsconfigJson = path.join(pathToApp, 'tsconfig.json'); const tsconfigJson = fs.readJSONSync(pathToTsconfigJson); + const toolingUtil = utils.loadToolingUtil(); + const preactPath = toolingUtil.getModulePath('./node_modules/preact', 'preact'); + const preactCompat = path.join(preactPath, 'compat', 'src', 'index.d.ts'); + tsconfigJson.compilerOptions.rootDir = `./${oraclejetConfigJson.paths.source.common}`; tsconfigJson.compilerOptions.outDir = `./${oraclejetConfigJson.paths.staging.web}`; tsconfigJson.compilerOptions.typeRoots.unshift('./types'); tsconfigJson.compilerOptions.resolveJsonModule = true; tsconfigJson.compilerOptions.esModuleInterop = true; tsconfigJson.compilerOptions.paths.react = [ - './node_modules/preact/compat/src/index.d.ts' + preactCompat ]; tsconfigJson.compilerOptions.paths['react-dom'] = [ - './node_modules/preact/compat/src/index.d.ts' + preactCompat ]; fs.writeJSONSync(pathToTsconfigJson, tsconfigJson, { spaces: 2 }); } diff --git a/common/template/index.js b/common/template/index.js index 7db6caf..023ce15 100644 --- a/common/template/index.js +++ b/common/template/index.js @@ -17,8 +17,6 @@ const constants = require('../../lib/util/constants'); const _HYBRID = 'hybrid'; const _WEB = 'web'; -const _TEMPLATES_NPM_URL = '@oracle/oraclejet-templates@~12.1.0'; - module.exports = { handleTemplate: function _handleTemplate(generator, templateDestDirectory) { @@ -42,7 +40,7 @@ function _getHandler(generator, template, templateDestDirectory) { return localTemplate.handle(generator, templateLocalPath, templateDestDirectory); } const templateSpec = _resolveTemplateSpec(generator, template); - return npmTemplate.handle(generator, _TEMPLATES_NPM_URL, templateDestDirectory, templateSpec); + return npmTemplate.handle(generator, templateDestDirectory, templateSpec); } function _isUrl(url) { diff --git a/common/template/npm.js b/common/template/npm.js index dfd564b..3ee22b1 100644 --- a/common/template/npm.js +++ b/common/template/npm.js @@ -8,20 +8,20 @@ const fs = require('fs-extra'); const path = require('path'); -const execSync = require('child_process').execSync; const utils = require('../../lib/util/utils'); const injectorUtils = require('../../lib/util/injectors'); -module.exports = { +const templateRoot = path.dirname(require.resolve('@oracle/oraclejet-templates')); - handle: function _handle(generator, npmUrl, destination, templateSpec) { +module.exports = { + handle: function _handle(generator, destination, templateSpec) { return new Promise((resolve, reject) => { - _installNpmTemplate(generator, npmUrl) - .then(() => { - _copyNpmTemplate(generator, templateSpec, destination); - return resolve(generator); - }) - .catch(err => reject(err)); + try { + _copyNpmTemplate(generator, templateSpec, destination); + resolve(generator); + } catch (err) { + reject(err); + } }); } }; @@ -42,10 +42,8 @@ module.exports = { * @param {string} destination - destination path */ function _copyNpmTemplate(generator, templateSpec, destination) { - const templateRoot = path.join(path.resolve(generator.appDir), - 'node_modules', '@oracle/oraclejet-templates'); - const src = _getTemplateFromTypeSpecificDirectory(templateRoot, templateSpec) || - _getTemplateFromGenericDirectory(templateRoot, templateSpec); + const src = _getTemplateFromTypeSpecificDirectory(templateSpec) || + _getTemplateFromGenericDirectory(templateSpec); if (!src) { const msg = `${templateSpec.name}:${templateSpec.type}`; @@ -124,7 +122,7 @@ function _copyNpmTemplate(generator, templateSpec, destination) { }); } -function _getTemplateFromTypeSpecificDirectory(templateRoot, templateSpec) { +function _getTemplateFromTypeSpecificDirectory(templateSpec) { const src = path.join(templateRoot, templateSpec.name, templateSpec.type); if (!_checkDirExists(src)) { return null; @@ -132,7 +130,7 @@ function _getTemplateFromTypeSpecificDirectory(templateRoot, templateSpec) { return src; } -function _getTemplateFromGenericDirectory(templateRoot, templateSpec) { +function _getTemplateFromGenericDirectory(templateSpec) { const src = path.join(templateRoot, templateSpec.name); if (!_checkDirExists(src)) { return null; @@ -152,17 +150,3 @@ function _checkDirExists(filePath) { } return false; } - -function _installNpmTemplate(generator, npmUrl) { - return new Promise((resolve) => { - const cmd = `npm install ${npmUrl}`; - try { - const appDir = path.resolve(generator.appDir); - fs.ensureDirSync(path.join(appDir, 'node_modules')); - execSync(cmd, { cwd: appDir, stdio: 'ignore' }); - } catch (err) { - utils.log.error(err); - } - resolve(); - }); -} diff --git a/config.js b/config.js index 33cd10a..7cf6243 100644 --- a/config.js +++ b/config.js @@ -343,7 +343,7 @@ const config = { aliases: ['packs'], description: 'Creates a pack with the specified name in an existing app', parameters: '', - examples: ['ojet create pack component demo-pack'] + examples: ['ojet create pack demo-pack'] }, theme: { aliases: ['themes'], diff --git a/generators/add-pcss-theme/index.js b/generators/add-pcss-theme/index.js index 4ab61f4..9326fef 100644 --- a/generators/add-pcss-theme/index.js +++ b/generators/add-pcss-theme/index.js @@ -15,7 +15,6 @@ const utils = require('../../lib/util/utils'); const commonMessages = require('../../common/messages'); const DEFAULT_THEME = 'mytheme'; -const JET_PCSS_SRC_PATH = 'node_modules/@oracle/oraclejet/dist/pcss/oj/'; const JET_VERSION_TOKEN = '<%= jetversion %>'; const THEMENAME_TOKEN = '<%= themename %>'; const COMPONENT_TOKEN = '<%= importcomponents %>'; @@ -41,9 +40,8 @@ function _setSettingsFileByTech(tech) { } function _getJETVersion() { - let getPackageJson = path.resolve('./node_modules/@oracle/oraclejet/package.json'); - getPackageJson = fs.readJsonSync(getPackageJson); - return getPackageJson.version; + const toolingUtil = utils.loadToolingUtil(); + return toolingUtil.getJETVersionV(toolingUtil.getJETVersion()); } function _getComponentsList(baseName) { @@ -52,12 +50,14 @@ function _getComponentsList(baseName) { const componentAll = ALL_COMP_RW_PATH + baseName + COMPONENT_LIST.all; const componentCommon = ALL_COMP_RW_PATH + baseName + COMPONENT_LIST.common; + const JET_PCSS_SRC_PATH = path.join(utils.loadToolingUtil().getOraclejetPath(), 'dist', 'pcss', 'oj'); + const srcAllCompPath = - path.join(JET_PCSS_SRC_PATH, `v${_getJETVersion()}`, componentAll); + path.join(JET_PCSS_SRC_PATH, _getJETVersion(), componentAll); const srcCommonCompPath = - path.join(JET_PCSS_SRC_PATH, `v${_getJETVersion()}`, componentCommon); + path.join(JET_PCSS_SRC_PATH, _getJETVersion(), componentCommon); const srcNotagAllCompPath = - path.join(JET_PCSS_SRC_PATH, `v${_getJETVersion()}`, componentNotag); + path.join(JET_PCSS_SRC_PATH, _getJETVersion(), componentNotag); let notagCompContent = fs.readFileSync(srcNotagAllCompPath, 'utf-8'); notagCompContent = notagCompContent.replace(replaceCopyright, '') @@ -80,7 +80,7 @@ function _getReplaceValuePairsArray(basetheme, tech) { return [ { findstr: new RegExp('@import\ \"\.\.\/utilities', 'g'), //eslint-disable-line - replacewith: `@import "../../../../node_modules/@oracle/oraclejet/dist/pcss/oj/v${_getJETVersion()}/utilities` + replacewith: `@import "../../../../node_modules/@oracle/oraclejet/dist/pcss/oj/${_getJETVersion()}/utilities` }, { findstr: new RegExp('.*\\$themeName.*'), @@ -112,8 +112,9 @@ function _injectDefaultValues(basetheme, destPath, tech) { function _copyCssSettingsFile(addTheme, destPath, setTechnology) { const whichTheme = addTheme.themeOptionValue; const srcSettings = _getSettingsFileByTech(whichTheme, setTechnology); + const JET_PCSS_SRC_PATH = path.join(utils.loadToolingUtil().getOraclejetPath(), 'dist', 'pcss', 'oj'); const srcPath = - path.join(JET_PCSS_SRC_PATH, `v${_getJETVersion()}`, ALL_COMP_RW_PATH, whichTheme, srcSettings); + path.join(JET_PCSS_SRC_PATH, _getJETVersion(), ALL_COMP_RW_PATH, whichTheme, srcSettings); const destSettingsFileName = _setSettingsFileByTech(setTechnology); const destSettingsPath = path.join(destPath, constants.DEFAULT_PCSS_THEME, destSettingsFileName); fs.copySync(srcPath, destSettingsPath); diff --git a/generators/add-theme/index.js b/generators/add-theme/index.js index 0613f55..1cc2261 100644 --- a/generators/add-theme/index.js +++ b/generators/add-theme/index.js @@ -15,7 +15,7 @@ const utils = require('../../lib/util/utils'); const commonMessages = require('../../common/messages'); const DEFAULT_THEME = 'mytheme'; -const JET_SCSS_SRC_PATH = 'node_modules/@oracle/oraclejet/dist/scss'; + const PLATFORM_TOKEN = '<%= platform %>'; const JET_VERSION_TOKEN = '<%= jetversion %>'; const THEMENAME_TOKEN = '<%= themename %>'; @@ -72,6 +72,7 @@ function _addSassTheme(addTheme) { _renameFilesAllPlatforms(themeName, themeDestPath); resolve(addTheme); } catch (err) { + utils.log.error(err); reject(); } }); @@ -118,9 +119,8 @@ function _isValidThemeName(string) { } function _getJetVersion() { - let packageJSON = path.resolve('./node_modules/@oracle/oraclejet/package.json'); - packageJSON = fs.readJsonSync(packageJSON); - return packageJSON.version; + const toolingUtil = utils.loadToolingUtil(); + return toolingUtil.getJETVersionV(toolingUtil.getJETVersion()); } // default marker <%= jetversion %> <%= themename %> <%= platform %> @@ -140,6 +140,8 @@ function _copySettingsFilesFromJETSrc(themeName, dest) { constants.SUPPORTED_PLATFORMS.forEach((platform) => { const platformPath = _getPlatformPath(platform); const srcSettingFileName = _getSrcSettingFileName(platform); + const JET_SCSS_SRC_PATH = path.join(utils.loadToolingUtil().getOraclejetPath(), 'dist', 'scss'); + const srcPath = path.join(JET_SCSS_SRC_PATH, platformPath, srcSettingFileName); const destSettingFileName = _getDestSettingFileName(DEFAULT_THEME, platform); diff --git a/generators/app/templates/common/package.json b/generators/app/templates/common/package.json index 9df06d6..a7590ec 100644 --- a/generators/app/templates/common/package.json +++ b/generators/app/templates/common/package.json @@ -3,13 +3,13 @@ "version": "1.0.0", "description": "An Oracle JavaScript Extension Toolkit(JET) web app", "dependencies": { - "@oracle/oraclejet": "~12.1.0" + "@oracle/oraclejet": "~13.0.0" }, "devDependencies": { "fs-extra": "^8.1.0", - "glob": "^7.1.1", + "glob": "7.2.0", "underscore": "^1.10.2", - "@oracle/oraclejet-tooling": "~12.1.0" + "@oracle/oraclejet-tooling": "~13.0.0" }, "engines": { "node": ">=12.21.0" diff --git a/generators/hybrid/templates/common/package.json b/generators/hybrid/templates/common/package.json index 40562bb..9daeb79 100644 --- a/generators/hybrid/templates/common/package.json +++ b/generators/hybrid/templates/common/package.json @@ -3,13 +3,13 @@ "version": "1.0.0", "description": "An Oracle JavaScript Extension Toolkit (JET) mobile app", "dependencies": { - "@oracle/oraclejet": "~12.1.0" + "@oracle/oraclejet": "~13.0.0" }, "devDependencies": { "fs-extra": "^8.1.0", - "glob": "^7.1.1", + "glob": "7.2.0", "underscore": "^1.10.2", - "@oracle/oraclejet-tooling": "~12.1.0" + "@oracle/oraclejet-tooling": "~13.0.0" }, "engines": { "node": ">=12.21.0" diff --git a/hybrid/index.js b/hybrid/index.js index e457437..ee1a15f 100644 --- a/hybrid/index.js +++ b/hybrid/index.js @@ -10,7 +10,7 @@ const fs = require('fs-extra'); const path = require('path'); const constants = require('../lib/util/constants'); const commonMessages = require('../common/messages'); -const DOMParser = require('xmldom').DOMParser; +const DOMParser = require('@xmldom/xmldom').DOMParser; const endOfLine = require('os').EOL; const graphics = require('./graphics'); const paths = require('../lib/util/paths'); diff --git a/lib/scopes/app.js b/lib/scopes/app.js index db3ba68..c70df21 100644 --- a/lib/scopes/app.js +++ b/lib/scopes/app.js @@ -59,7 +59,9 @@ app.createComponent = function (parameter, options) { }; app.createTheme = function (parameter, options) { + utils.validateSassInstall(); if (!utils.validCustomProperties()) { + utils.log.warning('The created theme defaults to alta as base-theme, which is deprecated.'); return addTheme(parameter, options); } return addPcssTheme(parameter, options); diff --git a/lib/util/utils.js b/lib/util/utils.js index a5ae0cb..d6fac8c 100644 --- a/lib/util/utils.js +++ b/lib/util/utils.js @@ -767,10 +767,9 @@ utils.validateServeOptions = (serveOptions, targetKey, platform) => { return customOptions; }; -utils.validCustomProperties = () => { - const customProp = fs.existsSync(`${path.resolve(process.cwd())}/node_modules/postcss-calc`); - return customProp; -}; +utils.validCustomProperties = () => utils.loadToolingUtil().getInstalledCssPackage(); + +utils.validateSassInstall = () => utils.loadToolingUtil().validateSassInstall(); utils.loadTooling = () => { const toolingPath = path.join(process.cwd(), constants.TOOLING_PATH); diff --git a/package.json b/package.json index 0bbc502..2371604 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@oracle/ojet-cli", - "version": "12.1.0", + "version": "13.0.0", "description": "Oracle JET Command Line Interface", "license": "UPL-1.0", "homepage": "http://www.oracle.com/jet", @@ -11,10 +11,12 @@ "dependencies": { "adm-zip": "~0.4.7", "fs-extra": "~8.1.0", - "inquirer": "~6.2.2", + "glob": "7.2.0", + "inquirer": "~8.2.2", "minimist": "~1.2.0", - "xmldom": "0.5.0", - "@oracle/oraclejet-tooling": "~12.1.0" + "@xmldom/xmldom": "0.8.1", + "@oracle/oraclejet-tooling": "~13.0.0", + "@oracle/oraclejet-templates": "~13.0.0" }, "engines": { "node": ">=12.21.0" diff --git a/template/component/js/component.json b/template/component/js/component.json index f297783..e387a15 100644 --- a/template/component/js/component.json +++ b/template/component/js/component.json @@ -1,7 +1,7 @@ { "name": "@component-name@", "version": "1.0.0", - "jetVersion": "^12.1.0", + "jetVersion": "^13.0.0", "displayName": "A user friendly, translatable name of the component.", "description": "A translatable high-level description for the component.", "properties": { diff --git a/template/component/ts/component.json b/template/component/ts/component.json index f297783..e387a15 100644 --- a/template/component/ts/component.json +++ b/template/component/ts/component.json @@ -1,7 +1,7 @@ { "name": "@component-name@", "version": "1.0.0", - "jetVersion": "^12.1.0", + "jetVersion": "^13.0.0", "displayName": "A user friendly, translatable name of the component.", "description": "A translatable high-level description for the component.", "properties": { diff --git a/template/component/tsx/@component@-functional-template.tsx b/template/component/tsx/@component@-functional-template.tsx index 0568e5f..006fbc0 100644 --- a/template/component/tsx/@component@-functional-template.tsx +++ b/template/component/tsx/@component@-functional-template.tsx @@ -1,5 +1,5 @@ -import { registerCustomElement } from "ojs/ojvcomponent"; -import { h } from "preact"; +import { ExtendGlobalProps, registerCustomElement } from "ojs/ojvcomponent"; +import { h, ComponentProps, ComponentType } from "preact"; import componentStrings = require("ojL10n!./resources/nls/@component-name@-strings"); import "css!./@component-name@-styles.css"; @@ -7,13 +7,21 @@ type Props = Readonly<{ message?: string; }>; -export const @camelcasecomponent-name@ = registerCustomElement("@full-component-name@", /** - * - * @ojmetadata version "1.0.0" - * @ojmetadata displayName "A user friendly, translatable name of the component" - * @ojmetadata description "A translatable high-level description for the component" - */ - ({ message = "Hello from @full-component-name@" }: Props) => { - return

{message}

- } +/** + * + * @ojmetadata version "1.0.0" + * @ojmetadata displayName "A user friendly, translatable name of the component" + * @ojmetadata description "A translatable high-level description for the component" + */ +function @camelcasecomponent-name@Impl( + { message = "Hello from @full-component-name@" }: Props +) { + return

{message}

+} + +export const @camelcasecomponent-name@: ComponentType< + ExtendGlobalProps> +> = registerCustomElement( + "@full-component-name@", + @camelcasecomponent-name@Impl ); \ No newline at end of file diff --git a/template/component/tsx/@component@.tsx b/template/component/tsx/@component@.tsx index f793a35..24146f0 100644 --- a/template/component/tsx/@component@.tsx +++ b/template/component/tsx/@component@.tsx @@ -3,9 +3,9 @@ import { h, Component, ComponentChild } from "preact"; import componentStrings = require("ojL10n!./resources/nls/@component-name@-strings"); import "css!./@component-name@-styles.css"; -type Props = { +type Props = Readonly<{ message?: string; -} +}>; /** * @ojmetadata pack "@pack-name@" @@ -19,7 +19,7 @@ export class @camelcasecomponent-name@ extends Component < ExtendGlobalProps < P message: "Hello from @full-component-name@!" }; -render(props: Readonly): ComponentChild { +render(props: Props): ComponentChild { return

{ props.message }

; } } diff --git a/template/component/tsx/README.md b/template/component/tsx/README.md index f518b2b..20b65e1 100644 --- a/template/component/tsx/README.md +++ b/template/component/tsx/README.md @@ -2,4 +2,4 @@ ## Usage Refer to the VComponent jsdoc -http://www.oracle.com/webfolder/technetwork/jet/jsdocs/VComponent.html \ No newline at end of file +http://www.oracle.com/webfolder/technetwork/jet/jsdocs/ojvcomponent.html \ No newline at end of file diff --git a/template/resource-component/component.json b/template/resource-component/component.json index 9d38e5b..ab055ae 100644 --- a/template/resource-component/component.json +++ b/template/resource-component/component.json @@ -2,7 +2,7 @@ "name": "@component-name@", "type": "resource", "version": "1.0.0", - "jetVersion": "^12.1.0", + "jetVersion": "^13.0.0", "displayName": "A user friendly, translatable name of the component.", "description": "A translatable high-level description for the component.", "publicModules": [ diff --git a/test/apiTest.js b/test/apiTest.js index 19bd086..5116115 100644 --- a/test/apiTest.js +++ b/test/apiTest.js @@ -41,7 +41,8 @@ describe('CLI API Tests', () => { try { await ojet.execute({ task: 'build' }); assert.ok(true); - } catch { + } catch (e) { + console.log(e); assert.ok(false); } }); @@ -60,7 +61,8 @@ describe('CLI API Tests', () => { try { await ojet.execute({ task: 'build', options: { release: true } }); assert.ok(true); - } catch { + } catch (e) { + console.log(e); assert.ok(false); } }); @@ -97,7 +99,8 @@ describe('CLI API Tests', () => { ); const exists = fs.existsSync(pathToZip); assert.ok(exists, pathToZip); - } catch { + } catch (e) { + console.log(e); assert.ok(false); } }); @@ -121,7 +124,8 @@ describe('CLI API Tests', () => { ); const exists = fs.existsSync(pathToZip); assert.ok(exists, pathToZip); - } catch { + } catch (e) { + console.log(e); assert.ok(false); } }); @@ -143,7 +147,8 @@ describe('CLI API Tests', () => { ); const exists = fs.existsSync(pathToZip); assert.ok(exists, pathToZip); - } catch { + } catch (e) { + console.log(e); assert.ok(false); } }); @@ -167,7 +172,8 @@ describe('CLI API Tests', () => { try { await ojet.execute({ task: 'strip' }); assert.ok(true); - } catch { + } catch (e) { + console.log(e); assert.ok(false); } }); @@ -202,7 +208,8 @@ describe('CLI API Tests', () => { try { await ojet.execute({ task: 'restore' }); assert.ok(true); - } catch { + } catch (e) { + console.log(e); assert.ok(false); } }); @@ -241,7 +248,8 @@ describe('CLI API Tests', () => { try { await ojet.execute({ task: 'strip' }); assert.ok(true); - } catch { + } catch (e) { + console.log(e); assert.ok(false); } }); diff --git a/test/componentTest.js b/test/componentTest.js index d9841ae..14871a8 100644 --- a/test/componentTest.js +++ b/test/componentTest.js @@ -10,6 +10,7 @@ const assert = require('assert'); const fs = require('fs-extra'); const util = require('./util'); const path = require('path'); +const _ = require('lodash'); const COMPONENT_NAME = 'comp-1'; const COPYING_TEST_COMPONENT_NAME = 'demo-card'; @@ -55,54 +56,240 @@ const COMPOSITE_COMPONENT_OPTIMIZED_FOLDER = 'min'; const STRIP_TEST_PACK_NAME = 'pack-strip'; const STRIP_TEST_COMPONENT_NAME = 'comp-strip'; const STRIP_TEST_COMPONENT_JSON = { - 'dynamicSlots' : {}, - 'properties' : { - 'enumValues' : {}, - 'properties' : {}, - 'readOnly' : {}, - 'type' : {}, - 'value' : {}, - 'binding' : {}, - 'writeback' : {}, - 'displayName': {}, - 'eventGroup' : {} + "displayName": "A user friendly, translatable name of the component.", + "description": "A translatable high-level description for the component.", + "properties": { + "helpHints": { + "displayName": "Help Hints", + "description": "Represents hints for oj-form-layout element to render help information on the label of the component", + "type": "object", + "translatable": true, + "properties": { + "definition": { + "displayName": "Defintion", + "description": "Hint for help definition text associated with the label", + "type": "string" + }, + "source": { + "displayName": "Source", + "description": "Hint for help source URL associated with the label.", + "type": "string" + } + } + }, + "readOnly": { + "displayName": "Readonly", + "description": "Defines if the calendar as a whole can be edited in any way, can be overridden by individual events", + "type": "boolean", + "propertyGroup": "common", + "extension": { + "calendarOption": "editable", + "transform": "invert" + } + }, + "displayOptions": { + "displayName": "Display Options", + "description": "Advanced options for configuring the calendar UI", + "type": "object", + "properties": { + "now": { + "displayName": "Current Date", + "description": "Date to use as the current date for the calendar, defaults to current date and time", + "type": "string", + "format": "date-time", + "propertyGroup": "data", + "extension": { + "catalog": { + "description": "I am to go!", + "check": { + "description": "Mamam" + } + }, + "oracle": { + "businessApprovals": { + "description": "I am a software engineer at Oracle" + }, + "type": "string" + }, + "vbdt": { + "description": "I am to go!" + }, + "audit": { + "thisData": { + "description": "Het", + "type": "string" + } + }, + "calendarOption": "now" + } + }, + "nowIndicator": { + "displayName": "Now Indicator", + "description": "Show a marker indicating the current date and time", + "type": "boolean", + "value": false, + "extension": { + "calendarOption": "nowIndicator" + } + }, + "weekends": { + "displayName": "Show Weekends", + "description": "display weekends on the calendar", + "type": "boolean", + "value": true, + "extension": { + "calendarOption": "weekends" + } + }, + "allowEventOverlap": { + "displayName": "Allow Overlap", + "description": "Controls if events are allowed to be edited to overlap in the schedule. Can be overridden on an event by event basis", + "type": "boolean", + "extension": { + "calendarOption": "eventOverlap" + } + }, + "height": { + "displayName": "Height", + "description": "Allows an explicit height to be set for the whole calendar content area, use -1 to auto size the calendar to fill the dimensions of the element", + "type": "number", + "extension": { + "calendarOption": "height" + } + }, + "locale": { + "displayName": "Locale", + "description": "Locale to show the calendar in, defaults to locale of page", + "type": "string", + "enumValues": ["af", "ar-dz", "ar-kw"], + "extension": { + "calendarOption": "locale" + } + } + } + } + }, + "methods": { + "focus": { + "internalName": "setFocus", + "description": "A function to set focus on the field", + "return": "boolean" + } }, - 'methods' : { - 'internalNames' : {}, - 'extension' : {}, - 'params' : {} + "events": { + "onclick": { + "bubbles": true, + "description": "Demo", + "status": [{ + "type": "deprecated" + }], + "cancelable": true + } + }, + "slots": { + "check": { + "description": "Demo", + "visible": false, + "extension": { + "vbdt": { + "description": "demo" + } + } + } + }, + "extension": { + "extension": { + "calendarOption": "locale" + }, + "catalog": { + "description": "Demo test.", + "check": { + "description": "Demo test." + } + }, + "oracle": { + "businessApprovals": { + "description": "Demo test." + } + }, + "vbdt": { + "description": "Demo test." + }, + "audit": { + "thisData": { + "description": "Demo test." + } + } } }; - -// Checks if the sub-properties on the required top level attributes are -// properly stripped: -function hasSubProperties({attribute, componentJSON}) { - const requiredMethodsAttributes = ['internalNames', 'extension']; - const requiredPropertiesAttributes = [ - 'enumValues', - 'properties', - 'readOnly', - 'type', - 'value', - 'binding', - 'writeback', - 'extension' - ]; - if (attribute === 'properties') { - return requiredPropertiesAttributes.every( - (subProperty) => { - componentJSON[attribute].hasOwnProperty(subProperty); - }); - } else { - return requiredMethodsAttributes.every( - (subProperty) => { - componentJSON[attribute].hasOwnProperty(subProperty); - }); +const EXPECTED_STRIPPED_JSON = { + "name": "comp-strip", + "version": "1.0.0", + "properties": { + "helpHints": { + "type": "object", + "properties": { + "definition": { + "type": "string" + }, + "source": { + "type": "string" + } + } + }, + "readOnly": { + "type": "boolean" + }, + "displayOptions": { + "type": "object", + "properties": { + "now": { + "type": "string", + "extension": { + "oracle": { + "type": "string" + }, + "audit": { + "thisData": { + "type": "string" + } + } + } + }, + "nowIndicator": { + "type": "boolean", + "value": false + }, + "weekends": { + "type": "boolean", + "value": true + }, + "allowEventOverlap": { + "type": "boolean" + }, + "height": { + "type": "number" + }, + "locale": { + "type": "string", + "enumValues": ["af", "ar-dz", "ar-kw"] + } + } + } + }, + "methods": { + "focus": { + "internalName": "setFocus" + } } -} - +}; // Returns the variables to used to run the test checks: -function getStripTestVariables({pathToLocalComponentInWeb, pathToLocalComponentInSrc, componentMinLoader}) { +function getStripTestVariables({ + pathToLocalComponentInWeb, + pathToLocalComponentInSrc, + componentMinLoader, + appName +}) { // Read and later on check that component.json in src and web are the same--meaning that the file is properly // restored in web: const componentJSONInWeb = fs.readJSONSync(pathToLocalComponentInWeb); @@ -110,23 +297,33 @@ function getStripTestVariables({pathToLocalComponentInWeb, pathToLocalComponentI const componentJSONInWebAttributes = Object.getOwnPropertyNames(componentJSONInWeb); const componentJSONInSrcAttributes = Object.getOwnPropertyNames(componentJSONInSrc); const webHasAllComponentJSONSrcAttributes = componentJSONInWebAttributes.every( - (attribute) => componentJSONInSrcAttributes.includes(attribute)); + (attribute) => componentJSONInSrcAttributes.includes(attribute)); const errorMessageForWeb = 'The component.json in web is not properly restored.'; // Retrieve the metadata in min/loader.js: - const loaderJsContent = fs.readFileSync(componentMinLoader, { encoding: 'utf-8' }); + const loaderJsContent = fs.readFileSync(componentMinLoader, { + encoding: 'utf-8' + }); const regex = /component\.json",\s*\[\],\s*\(function\(\){return'(?.*)'}\)\)/gm; const match = regex.exec(loaderJsContent); - const modifiedComponentJSON = JSON.parse(match.groups.componentJSONInLoader.replace(/\\n/g, '')); + const modifiedComponentJSON = JSON.parse(match.groups.componentJSONInLoader.replace(/\\n/g, '')); const modifiedComponentJSONAttributes = Object.getOwnPropertyNames(modifiedComponentJSON); - const loaderHasStrippedAttributes = !componentJSONInWebAttributes.every( - (attribute) => { - if (attribute == 'properties' || attribute == 'methods') { - hasSubProperties({attribute, modifiedComponentJSON}) && - modifiedComponentJSONAttributes.includes(attribute); - } else { - modifiedComponentJSONAttributes.includes(attribute); - } - }); + let loaderHasStrippedAttributes; + if (componentJSONInWeb.hasOwnProperty('pack')) { + loaderHasStrippedAttributes = _.isEqual(modifiedComponentJSON, { + ...EXPECTED_STRIPPED_JSON, + ...{ + jetVersion: componentJSONInWeb['jetVersion'], + pack: componentJSONInWeb['pack'] + } + }); + } else { + loaderHasStrippedAttributes = _.isEqual(modifiedComponentJSON, { + ...EXPECTED_STRIPPED_JSON, + ...{ + jetVersion: componentJSONInWeb['jetVersion'], + } + }); + } const errorMessageForLoader = 'The loader.js in min has no stripped metadata.'; return { webHasAllComponentJSONSrcAttributes, @@ -140,41 +337,70 @@ describe('Component & Jet Pack Tests', () => { before(async () => { if (!util.noScaffold()) { util.removeAppDir(util.COMPONENT_APP_NAME); - + // Scaffold component js app from scratch - let result = await util.execCmd(`${util.OJET_COMMAND} create ${util.COMPONENT_APP_NAME} --use-global-tooling --template=navbar`, { cwd: util.testDir }); + let result = await util.execCmd(`${util.OJET_COMMAND} create ${util.COMPONENT_APP_NAME} --use-global-tooling --template=navbar`, { + cwd: util.testDir + }); console.log(result.stdout); // Check output // assert.equal(util.norestoreSuccess(result.stdout) || /Your app is/.test(result.stdout), true, result.error); // Set exchange url for exchange-related tests - result = await util.execCmd(`${util.OJET_APP_COMMAND} configure --exchange-url=${util.EXCHANGE_URL}`, { cwd: util.getAppDir(util.COMPONENT_APP_NAME) }); + result = await util.execCmd(`${util.OJET_APP_COMMAND} configure --exchange-url=${util.EXCHANGE_URL}`, { + cwd: util.getAppDir(util.COMPONENT_APP_NAME) + }); console.log(result.stdout); - } - + } + if (!util.noScaffold()) { util.removeAppDir(util.COMPONENT_TS_APP_NAME); - + // Scaffold component ts app from scratch - let result = await util.execCmd(`${util.OJET_COMMAND} create ${util.COMPONENT_TS_APP_NAME} --use-global-tooling --template=navbar --typescript`, { cwd: util.testDir }); + let result = await util.execCmd(`${util.OJET_COMMAND} create ${util.COMPONENT_TS_APP_NAME} --use-global-tooling --template=navbar --typescript`, { + cwd: util.testDir + }); console.log(result.stdout); // Check output // assert.equal(util.norestoreSuccess(result.stdout) || /Your app is/.test(result.stdout), true, result.error); // Set exchange url for exchange-related tests - result = await util.execCmd(`${util.OJET_APP_COMMAND} configure --exchange-url=${util.EXCHANGE_URL}`, { cwd: util.getAppDir(util.COMPONENT_TS_APP_NAME) }); + result = await util.execCmd(`${util.OJET_APP_COMMAND} configure --exchange-url=${util.EXCHANGE_URL}`, { + cwd: util.getAppDir(util.COMPONENT_TS_APP_NAME) + }); console.log(result.stdout); } }); describe('Component Tests', () => { - function execComponentCommand({ task, app, component, flags = '' }) { - return util.execCmd(`${util.OJET_APP_COMMAND} ${task} component ${component} ${flags}`, { cwd: util.getAppDir(app) }, true, true); + function execComponentCommand({ + task, + app, + component, + flags = '' + }) { + return util.execCmd(`${util.OJET_APP_COMMAND} ${task} component ${component} ${flags}`, { + cwd: util.getAppDir(app) + }, true, true); } - - function beforeComponentTest({ task, app, component, flags, componentJson, scriptsFolder }) { + + function beforeComponentTest({ + task, + app, + component, + flags, + componentJson, + scriptsFolder + }) { before(async () => { - await execComponentCommand({ task, app, component, flags }); + await execComponentCommand({ + task, + app, + component, + flags + }); if (componentJson) { - const { pathToSourceComponents } = util.getAppPathData(app, scriptsFolder); + const { + pathToSourceComponents + } = util.getAppPathData(app, scriptsFolder); const pathToComponentComponentJson = path.join( pathToSourceComponents, component, @@ -182,9 +408,12 @@ describe('Component & Jet Pack Tests', () => { ); const componentComponentJson = fs.readJsonSync(pathToComponentComponentJson); fs.writeJsonSync( - pathToComponentComponentJson, - {...componentComponentJson, ...componentJson }, - { spaces: 2 } + pathToComponentComponentJson, { + ...componentComponentJson, + ...componentJson + }, { + spaces: 2 + } ); } }); @@ -197,13 +426,26 @@ describe('Component & Jet Pack Tests', () => { // Also we add a dependency on the exchange component. // In a subsequent (release build) test, this will verify the dependency on a pack component. // - function createComponentTypeCompositeTest({ appName, scriptsFolder, component, componentJson }) { + function createComponentTypeCompositeTest({ + appName, + scriptsFolder, + component, + componentJson + }) { if (!util.noScaffold()) { - beforeComponentTest({ task: 'create', app: appName, component, componentJson, scriptsFolder }); + beforeComponentTest({ + task: 'create', + app: appName, + component, + componentJson, + scriptsFolder + }); } describe('check created component', () => { it(`should have ${appName}/src/${scriptsFolder}/${component}/jet-composites/component.json`, () => { - const { pathToSourceComponents } = util.getAppPathData(appName, scriptsFolder); + const { + pathToSourceComponents + } = util.getAppPathData(appName, scriptsFolder); const pathToComponentJson = path.join( pathToSourceComponents, component, @@ -218,13 +460,26 @@ describe('Component & Jet Pack Tests', () => { // // Create a component with type:demo. // - function createComponentTypeDemoTest({ appName, scriptsFolder, component, componentJson }) { + function createComponentTypeDemoTest({ + appName, + scriptsFolder, + component, + componentJson + }) { if (!util.noScaffold()) { - beforeComponentTest({ task: 'create', app: appName, component, scriptsFolder, componentJson }); + beforeComponentTest({ + task: 'create', + app: appName, + component, + scriptsFolder, + componentJson + }); } describe('check created demo component', () => { it(`should have ${appName}/src/${scriptsFolder}/${component}/jet-composites/component.json`, () => { - const { pathToSourceComponents } = util.getAppPathData(appName, scriptsFolder); + const { + pathToSourceComponents + } = util.getAppPathData(appName, scriptsFolder); const pathToComponentJson = path.join( pathToSourceComponents, component, @@ -236,9 +491,18 @@ describe('Component & Jet Pack Tests', () => { }); } - function createVComponentTest({ appName, scriptsFolder, component }) { + function createVComponentTest({ + appName, + scriptsFolder, + component + }) { if (!util.noScaffold()) { - beforeComponentTest({ task: 'create', app: appName, component, flags: '--vcomponent' }); + beforeComponentTest({ + task: 'create', + app: appName, + component, + flags: '--vcomponent' + }); } describe('check created vcomponent', () => { const pathToComponent = util.getAppDir(path.join( @@ -255,16 +519,26 @@ describe('Component & Jet Pack Tests', () => { }); it('should not have @ojmetadata pack "@pack-name@" jsdoc', () => { const packRegex = new RegExp('@ojmetadata pack', 'g'); - const componentContent = fs.readFileSync(pathToComponent, { encoding: 'utf-8' }); + const componentContent = fs.readFileSync(pathToComponent, { + encoding: 'utf-8' + }); const hasPack = !!packRegex.exec(componentContent); assert.ok(!hasPack, 'singleton vcomponent has @ojmetadata pack jsdoc'); }); }); } - function addComponentTest({ appName, scriptsFolder, component }) { + function addComponentTest({ + appName, + scriptsFolder, + component + }) { if (!util.noScaffold()) { - beforeComponentTest({ task: 'add', app: appName, component }); + beforeComponentTest({ + task: 'add', + app: appName, + component + }); } describe('check added component', () => { it(`should have ${appName}/jet_components/${EXCHANGE_COMPONENT_PACK}/${EXCHANGE_COMPONENT_PACK_MEMBER}/component.json`, () => { @@ -325,9 +599,16 @@ describe('Component & Jet Pack Tests', () => { }); }*/ - function buildComponentTest({ appName, component }) { + function buildComponentTest({ + appName, + component + }) { if (!util.noScaffold()) { - beforeComponentTest({ task: 'build', app: appName, component }); + beforeComponentTest({ + task: 'build', + app: appName, + component + }); } describe('check built component', () => { const appDir = util.getAppDir(appName); @@ -342,6 +623,16 @@ describe('Component & Jet Pack Tests', () => { const exists = fs.pathExistsSync(typesDir); assert.ok(exists, typesDir); }); + it(`should have an apidoc json file in ${appName}/web/js/jet-composites/${component}/${DEFAULT_COMPONENT_VERSION}`, () => { + const apiDocFile = path.join(appDir, 'web', 'js', 'jet-composites', component, DEFAULT_COMPONENT_VERSION, 'Vcomp1.json'); + const exists = fs.pathExistsSync(apiDocFile); + assert.ok(exists, apiDocFile); + }); + it(`should have a "properties" entry in ${appName}/web/js/jet-composites/${component}/${DEFAULT_COMPONENT_VERSION} indicating component.json generated by the compiler`, () => { + const pathToComponentJSON = path.join(appDir, 'web', 'js', 'jet-composites', component, DEFAULT_COMPONENT_VERSION, 'component.json'); + const componentJson = fs.readJsonSync(pathToComponentJSON); + assert.ok(componentJson.properties, "Properties not found in component.json"); + }); } }) } @@ -352,47 +643,68 @@ describe('Component & Jet Pack Tests', () => { // (note that type:demo components are not minified, thus the // path mapping should't be to /min for type:demo components). // - function releaseBuildComponentTypeDemoTest({ appName, component }) { + function releaseBuildComponentTypeDemoTest({ + appName, + component + }) { describe('check type:demo component', () => { if (!util.noBuild()) { const appDir = util.getAppDir(appName); it('should build release js app (type:demo) ', async () => { - const result = await util.execCmd(`${util.OJET_APP_COMMAND} build --release`, { cwd: appDir }); + const result = await util.execCmd(`${util.OJET_APP_COMMAND} build --release`, { + cwd: appDir + }); assert.equal(util.buildSuccess(result.stdout), true, result.error); }); // Verify that the path mapping for the demo component in the bundle // points to the debug area. it('release build: path mapping to debug type:demo component', async () => { - const{ pathToBundleJs } = util.getAppPathData(appName); + const { + pathToBundleJs + } = util.getAppPathData(appName); const bundleContent = fs.readFileSync(pathToBundleJs); - + assert.equal(bundleContent.toString().match(`jet-composites/${component}/1.0.0`), `jet-composites/${component}/1.0.0`, - `bundle.js should contain the debug component ${component}`); - + `bundle.js should contain the debug component ${component}`); + assert.equal(bundleContent.toString().match(`jet-composites/${component}/1.0.0/min`), null, - `bundle.js should not contain the minified component ${component}`); + `bundle.js should not contain the minified component ${component}`); }); } }); } - function buildReleaseExchangeComponentTest({ appName, pack }) { + function buildReleaseExchangeComponentTest({ + appName, + pack + }) { describe('check that build release command on exchange components with bundle definitions runs successfully', () => { it('should successfully build and release an app using exchange component with bundle definitions', async () => { const appDir = util.getAppDir(appName); - await util.execCmd(`${util.OJET_APP_COMMAND} add component ${pack}`, { cwd: appDir }, true, true); - const result = await util.execCmd(`${util.OJET_APP_COMMAND} build --release`, { cwd: appDir }, true, true); + await util.execCmd(`${util.OJET_APP_COMMAND} add component ${pack}`, { + cwd: appDir + }, true, true); + const result = await util.execCmd(`${util.OJET_APP_COMMAND} build --release`, { + cwd: appDir + }, true, true); assert.equal(util.buildSuccess(result.stdout), true, result.error); }); }); } - function packageComponentTest({ appName, component }) { + function packageComponentTest({ + appName, + component + }) { if (!util.noScaffold()) { - beforeComponentTest({ task: 'package', app: appName, component }); + beforeComponentTest({ + task: 'package', + app: appName, + component + }); } describe('check packaged component', () => { it(`should be packaged in ${appName}/dist/${component}.zip`, () => { @@ -407,19 +719,40 @@ describe('Component & Jet Pack Tests', () => { }) } - function packageComponentHookTest({ appName, component, scriptsFolder}) { + function packageComponentHookTest({ + appName, + component, + scriptsFolder + }) { describe('check that components are packaged through the hooks successfully', () => { - it('should package the component through the hooks', async() => { + it('should package the component through the hooks', async () => { const appDir = util.getAppDir(appName); - const {beforePackageHookPath, afterPackageHookPath, defaultBeforeHookContent, defaultAfterHookContent} = util.getHooksPathAndContent(appName); + const { + beforePackageHookPath, + afterPackageHookPath, + defaultBeforeHookContent, + defaultAfterHookContent + } = util.getHooksPathAndContent(appName); // write custom hooks file content for testing: - util.writeCustomHookContents({hookName:'before', filePath: beforePackageHookPath}); - util.writeCustomHookContents({hookName:'after', filePath: afterPackageHookPath}); + util.writeCustomHookContents({ + hookName: 'before', + filePath: beforePackageHookPath + }); + util.writeCustomHookContents({ + hookName: 'after', + filePath: afterPackageHookPath + }); // create the component and pack to package: - await util.execCmd(`${util.OJET_APP_COMMAND} create component ${component}`, { cwd: appDir}, true, true); - const result = await util.execCmd(`${util.OJET_APP_COMMAND} package component ${component}`, { cwd: appDir }, true, true); + await util.execCmd(`${util.OJET_APP_COMMAND} create component ${component}`, { + cwd: appDir + }, true, true); + const result = await util.execCmd(`${util.OJET_APP_COMMAND} package component ${component}`, { + cwd: appDir + }, true, true); // Delete created component - const { pathToSourceComponents } = util.getAppPathData(appName, scriptsFolder); + const { + pathToSourceComponents + } = util.getAppPathData(appName, scriptsFolder); fs.removeSync(path.join(pathToSourceComponents, component)); // Revert default hook content fs.writeFileSync(beforePackageHookPath, defaultBeforeHookContent); @@ -428,12 +761,17 @@ describe('Component & Jet Pack Tests', () => { assert.ok(/Running before_component_package for component: component being packaged is package-hooks-component/.test(result.stdout), result.stdout); assert.ok(/Running after_component_package for component: component being packaged is package-hooks-component/.test(result.stdout), result.stdout); }); - }); + }); } // Run the build, then verify that components have been properly built. // Note that we verify multiple components - component is an array of components. - function buildComponentAppTest({ appName, component, release, scriptsFolder }) { + function buildComponentAppTest({ + appName, + component, + release, + scriptsFolder + }) { const testName = release ? 'Build (Release)' : 'Build'; const buildType = release ? 'release' : 'default'; describe(testName, () => { @@ -441,7 +779,9 @@ describe('Component & Jet Pack Tests', () => { it(`should build ${buildType} component app`, async () => { let command = `${util.OJET_APP_COMMAND} build`; command = release ? command + ' --release' : command; - let result = await util.execCmd(command, { cwd: util.getAppDir(appName) }, true, true); + let result = await util.execCmd(command, { + cwd: util.getAppDir(appName) + }, true, true); assert.equal(util.buildSuccess(result.stdout), true, result.error); }); } @@ -449,9 +789,10 @@ describe('Component & Jet Pack Tests', () => { component.filter(_component => scriptsFolder === 'js' ? _component !== VCOMPONENT_NAME : true).forEach((individualComponent) => { const appDir = util.getAppDir(appName); const componentsDir = path.join(appDir, 'web', 'js', 'jet-composites'); - const componentLoader = path.join(componentsDir, individualComponent, DEFAULT_COMPONENT_VERSION,'loader.js'); + const componentLoader = path.join(componentsDir, individualComponent, DEFAULT_COMPONENT_VERSION, 'loader.js'); const componentMinDir = path.join(componentsDir, individualComponent, DEFAULT_COMPONENT_VERSION, 'min'); const componentMinLoader = path.join(componentMinDir, 'loader.js'); + const componentMinLoaderMapFile = componentMinLoader.replace('loader.js', 'loader.js.map'); if (release) { it(`component ${individualComponent} should have component(s) with /min directory`, () => { const exists = fs.pathExistsSync(componentMinDir); @@ -461,6 +802,14 @@ describe('Component & Jet Pack Tests', () => { const exists = fs.pathExistsSync(componentMinLoader); assert.ok(exists, componentMinLoader); }) + it(`component ${individualComponent} should have source field with file path in min/loader.js.map`, () => { + const sourceMapFileContent = fs.readFileSync(componentMinLoaderMapFile, {encoding : 'utf-8'}); + const sourceMapJsonObject = JSON.parse(sourceMapFileContent); + // If there is no file path, terser emits a string "0" in the array field sources: + const sourceMapHasSourceField = sourceMapJsonObject.sources[0] !== "0"; + const errorMessage = `component ${individualComponent} has no sources field with a file path.`; + assert.ok(sourceMapHasSourceField, errorMessage); + }) } else { it(`component ${individualComponent} should not have component(s) with /min directory`, () => { const exists = fs.pathExistsSync(componentMinDir); @@ -482,17 +831,31 @@ describe('Component & Jet Pack Tests', () => { }) } - function preferLocalOverExchangeComponentTest({ appName, scriptsFolder, component}) { + function preferLocalOverExchangeComponentTest({ + appName, + scriptsFolder, + component + }) { describe('check that only components in the src folder are copied to staging incase they are both in src and exchange', () => { - it('should prioritize components in the source folder on executing ojet build command', async() => { + it('should prioritize components in the source folder on executing ojet build command', async () => { const appDir = util.getAppDir(appName); - const {pathToExchangeComponents, pathToSourceComponents, pathToBuiltComponents} = util.getAppPathData(appName, scriptsFolder); + const { + pathToExchangeComponents, + pathToSourceComponents, + pathToBuiltComponents + } = util.getAppPathData(appName, scriptsFolder); // add a component from exchange: - await util.execCmd(`${util.OJET_APP_COMMAND} add component ${component}`, { cwd: appDir}, true, true); + await util.execCmd(`${util.OJET_APP_COMMAND} add component ${component}`, { + cwd: appDir + }, true, true); // create a component with the same name as exchange's: - await util.execCmd(`${util.OJET_APP_COMMAND} create component ${component}`, { cwd: appDir}, true, true); + await util.execCmd(`${util.OJET_APP_COMMAND} create component ${component}`, { + cwd: appDir + }, true, true); // run the ojet build command: - const result = await util.execCmd(`${util.OJET_APP_COMMAND} build`, { cwd: appDir}, true, true); + const result = await util.execCmd(`${util.OJET_APP_COMMAND} build`, { + cwd: appDir + }, true, true); assert.equal(util.buildSuccess(result.stdout), true, result.error); const pathToLocalComponentInWeb = path.join(pathToBuiltComponents, component, DEFAULT_COMPONENT_VERSION); const localComponentExistsInWeb = fs.pathExistsSync(pathToLocalComponentInWeb); @@ -509,10 +872,12 @@ describe('Component & Jet Pack Tests', () => { assert.ok(localComponentExistsInWeb, pathToLocalComponentInWeb); assert.ok(!exchangeComponentExistsInWeb, pathToExhangeComponentInWeb); }); - }); + }); } - - function buildTsComponentAppWithDeclarationFalse({ appName }) { + + function buildTsComponentAppWithDeclarationFalse({ + appName + }) { describe('Build (declaration = false)', () => { const appDir = util.getAppDir(appName); if (!util.noBuild()) { @@ -521,13 +886,19 @@ describe('Component & Jet Pack Tests', () => { const tsconfigJsonPath = path.join(appDir, 'tsconfig.json'); const tsconfigJson = fs.readJsonSync(tsconfigJsonPath); tsconfigJson.compilerOptions.declaration = false; - fs.writeJsonSync(tsconfigJsonPath, tsconfigJson, { spaces: 2 }); + fs.writeJsonSync(tsconfigJsonPath, tsconfigJson, { + spaces: 2 + }); // build typescript component app const command = `${util.OJET_APP_COMMAND} build`; - const result = await util.execCmd(command, { cwd: util.getAppDir(appName) }, true, true); + const result = await util.execCmd(command, { + cwd: util.getAppDir(appName) + }, true, true); // set declaration back to true for downstream tests tsconfigJson.compilerOptions.declaration = true; - fs.writeJsonSync(tsconfigJsonPath, tsconfigJson, { spaces: 2 }); + fs.writeJsonSync(tsconfigJsonPath, tsconfigJson, { + spaces: 2 + }); assert.equal(util.buildSuccess(result.stdout), true, result.error); }); it(`should not have /types folder in ${appName}/web/js/jet-composites/${VCOMPONENT_NAME}`, () => { @@ -539,53 +910,78 @@ describe('Component & Jet Pack Tests', () => { }); } - function stripMetadatainMinLoaderComponentTest({ appName, scriptsFolder, component}) { + function stripMetadatainMinLoaderComponentTest({ + appName, + scriptsFolder, + component + }) { // The STRIP_TEST_COMPONENT_JSON will be merged with those in component.json in case it is not present. // We need it to ensure that all needed attributes at run-time are present. Here, we have // added some attributes that are not needed at RT to ensure that stripping is done correctly. if (!util.noScaffold()) { - beforeComponentTest({ task: 'create', app: appName, component, componentJson: STRIP_TEST_COMPONENT_JSON, scriptsFolder }) + beforeComponentTest({ + task: 'create', + app: appName, + component, + componentJson: STRIP_TEST_COMPONENT_JSON, + scriptsFolder + }) } describe('check that stripped metadata is in min/loader.js but not in staging', () => { - it('should have stripped metadata in min/loader.js', async() => { + it('should have stripped metadata in min/loader.js', async () => { const appDir = util.getAppDir(appName); - const {pathToSourceComponents, pathToBuiltComponents} = util.getAppPathData(appName, scriptsFolder); + const { + pathToSourceComponents, + pathToBuiltComponents + } = util.getAppPathData(appName, scriptsFolder); const pathToLocalComponentInWeb = path.join(pathToBuiltComponents, component, DEFAULT_COMPONENT_VERSION, 'component.json'); const pathToLocalComponentInSrc = path.join(pathToSourceComponents, component, 'component.json'); const componentMinDir = pathToLocalComponentInWeb.replace('component.json', COMPOSITE_COMPONENT_OPTIMIZED_FOLDER); const componentMinLoader = path.join(componentMinDir, COMPOSITE_COMPONENT_OPTIMIZED_FILE); // The required list of required attributes at run-time is: 'properties', 'methods', 'events', 'slots', and 'dynamicSlots'. // Since other attributes are pre defined, then we will be adding only dynamicSlots which is not common: - const result = await util.execCmd(`${util.OJET_APP_COMMAND} build --release`, { cwd: appDir}, true, true); + const result = await util.execCmd(`${util.OJET_APP_COMMAND} build --release`, { + cwd: appDir + }, true, true); assert.equal(util.buildSuccess(result.stdout), true, result.error); // Get the test variables to check: - const { - webHasAllComponentJSONSrcAttributes, - errorMessageForWeb, - loaderHasStrippedAttributes, - errorMessageForLoader - } = getStripTestVariables({pathToLocalComponentInWeb, pathToLocalComponentInSrc, componentMinLoader}); + const { + webHasAllComponentJSONSrcAttributes, + errorMessageForWeb, + loaderHasStrippedAttributes, + errorMessageForLoader + } = getStripTestVariables({ + pathToLocalComponentInWeb, + pathToLocalComponentInSrc, + componentMinLoader + }); // Delete the created components in the src and exchange folders: fs.removeSync(path.join(pathToSourceComponents, component)); // Check the results: assert.equal(loaderHasStrippedAttributes, true, errorMessageForLoader); assert.equal(webHasAllComponentJSONSrcAttributes, true, errorMessageForWeb); }); - }); + }); } - function omitComponentVerstionTest({ appName }) { + function omitComponentVerstionTest({ + appName + }) { describe(`Build ${appName} with --${util.OMIT_COMPONENT_VERSION_FLAG}`, () => { const components = [COMPONENT_NAME, COMPONENT_NAME_COMPOSITE, COMPONENT_NAME_DEMO, VCOMPONENT_NAME, EXCHANGE_COMPONENT_PACK]; if (!util.noBuild()) { it(`should build ${appName} with --${util.OMIT_COMPONENT_VERSION_FLAG}`, async () => { const command = `${util.OJET_APP_COMMAND} build --${util.OMIT_COMPONENT_VERSION_FLAG}`; - const result = await util.execCmd(command, { cwd: util.getAppDir(appName) }, true, false); + const result = await util.execCmd(command, { + cwd: util.getAppDir(appName) + }, true, false); assert.ok(util.buildSuccess(result.stdout), result.error); }); } it(`should build ${components} without a version folder`, () => { - const { pathToBuiltComponents } = util.getAppPathData(appName); + const { + pathToBuiltComponents + } = util.getAppPathData(appName); components.forEach(component => { const pathToBuiltComponent = path.join(pathToBuiltComponents, component); const componentExists = fs.existsSync(pathToBuiltComponent); @@ -597,7 +993,11 @@ describe('Component & Jet Pack Tests', () => { }); }); it(`should build main.js without versioned path mappings for ${components}`, () => { - const { pathToBuiltComponents, pathToMainJs, componentsFolder } = util.getAppPathData(appName); + const { + pathToBuiltComponents, + pathToMainJs, + componentsFolder + } = util.getAppPathData(appName); const mainJs = fs.readFileSync(pathToMainJs); components.forEach(component => { const pathToBuiltComponent = path.join(pathToBuiltComponents, component); @@ -614,16 +1014,26 @@ describe('Component & Jet Pack Tests', () => { if (!util.noBuild()) { it(`should release build ${appName} with --${util.OMIT_COMPONENT_VERSION_FLAG}`, async () => { const command = `${util.OJET_APP_COMMAND} build --${util.OMIT_COMPONENT_VERSION_FLAG} --release=true`; - const result = await util.execCmd(command, { cwd: util.getAppDir(appName) }, true, false); + const result = await util.execCmd(command, { + cwd: util.getAppDir(appName) + }, true, false); assert.ok(util.buildSuccess(result.stdout), result.error); }); } }); } - function createComponentTest({ appName, scriptsFolder, component }) { + function createComponentTest({ + appName, + scriptsFolder, + component + }) { if (!util.noScaffold()) { - beforeComponentTest({ task: 'create', app: appName, component }); + beforeComponentTest({ + task: 'create', + app: appName, + component + }); } describe('check created component', () => { it(`should have ${appName}/src/${scriptsFolder}/jet-composites/${component}/component.json`, () => { @@ -678,22 +1088,35 @@ describe('Component & Jet Pack Tests', () => { } }); } - + describe('ojet create component', () => { - function createComponentFailureTest({ appName, component }) { + function createComponentFailureTest({ + appName, + component + }) { describe('check component create failure', () => { - it ('should fail with "Invalid component name:"', async () => { + it('should fail with "Invalid component name:"', async () => { const task = 'create'; - const result = await execComponentCommand({ task, app: appName, component }); - assert.ok(util[`${task}ComponentFailure`]({ component, stdout: result.stdout }), result.error); + const result = await execComponentCommand({ + task, + app: appName, + component + }); + assert.ok(util[`${task}ComponentFailure`]({ + component, + stdout: result.stdout + }), result.error); }) }) } describe('valid name', () => { - util.runComponentTestInAllTestApps({ test: createComponentTest, component: COMPONENT_NAME }); - util.runComponentTestInAllTestApps({ - test: createComponentTypeCompositeTest, + util.runComponentTestInAllTestApps({ + test: createComponentTest, + component: COMPONENT_NAME + }); + util.runComponentTestInAllTestApps({ + test: createComponentTypeCompositeTest, component: COMPONENT_NAME_COMPOSITE, componentJson: { type: 'composite', @@ -702,53 +1125,64 @@ describe('Component & Jet Pack Tests', () => { } } }); - util.runComponentTestInAllTestApps({ - test: createComponentTypeDemoTest, + util.runComponentTestInAllTestApps({ + test: createComponentTypeDemoTest, component: COMPONENT_NAME_DEMO, componentJson: { type: 'demo' } }); util.runComponentTestInTestApp( - util.TYPESCRIPT_COMPONENT_APP_CONFIG, - { - test: createVComponentTest, + util.TYPESCRIPT_COMPONENT_APP_CONFIG, { + test: createVComponentTest, component: VCOMPONENT_NAME } ); }); describe('no hyphen in name', () => { - util.runComponentTestInAllTestApps({ test: createComponentFailureTest, component: 'comp1' }); + util.runComponentTestInAllTestApps({ + test: createComponentFailureTest, + component: 'comp1' + }); }); describe('capital letter in name', () => { - util.runComponentTestInAllTestApps({ test: createComponentFailureTest, component: 'Comp-1' }); + util.runComponentTestInAllTestApps({ + test: createComponentFailureTest, + component: 'Comp-1' + }); }); }); describe('ojet add component', () => { - util.runComponentTestInAllTestApps({ test: addComponentTest, component: EXCHANGE_COMPONENT_NAME }); + util.runComponentTestInAllTestApps({ + test: addComponentTest, + component: EXCHANGE_COMPONENT_NAME + }); }); describe('ojet build component', () => { - util.runComponentTestInAllTestApps({ test: buildComponentTest, component: COMPONENT_NAME }); + util.runComponentTestInAllTestApps({ + test: buildComponentTest, + component: COMPONENT_NAME + }); util.runComponentTestInTestApp( - util.TYPESCRIPT_COMPONENT_APP_CONFIG, - { - test: buildComponentTest, + util.TYPESCRIPT_COMPONENT_APP_CONFIG, { + test: buildComponentTest, component: VCOMPONENT_NAME } ); util.runComponentTestInTestApp( - util.JAVASCRIPT_COMPONENT_APP_CONFIG, - { - test: releaseBuildComponentTypeDemoTest, - component: COMPONENT_NAME_DEMO - } + util.JAVASCRIPT_COMPONENT_APP_CONFIG, { + test: releaseBuildComponentTypeDemoTest, + component: COMPONENT_NAME_DEMO + } ); }); describe('ojet package component', () => { - util.runComponentTestInAllTestApps({ test: packageComponentTest, component: COMPONENT_NAME }); - util.runComponentTestInTestApp( - util.TYPESCRIPT_COMPONENT_APP_CONFIG, - { + util.runComponentTestInAllTestApps({ + test: packageComponentTest, + component: COMPONENT_NAME + }); + util.runComponentTestInTestApp( + util.TYPESCRIPT_COMPONENT_APP_CONFIG, { test: packageComponentTest, component: VCOMPONENT_NAME } @@ -760,24 +1194,36 @@ describe('Component & Jet Pack Tests', () => { component: 'package-hooks-component' }); }); - + describe('ojet build', () => { - util.runComponentTestInAllTestApps({ test: buildComponentAppTest, component: [COMPONENT_NAME, COMPONENT_NAME_COMPOSITE, VCOMPONENT_NAME], release: false }); + util.runComponentTestInAllTestApps({ + test: buildComponentAppTest, + component: [COMPONENT_NAME, COMPONENT_NAME_COMPOSITE, VCOMPONENT_NAME], + release: false + }); util.runComponentTestInTestApp( - util.TYPESCRIPT_COMPONENT_APP_CONFIG, - { + util.TYPESCRIPT_COMPONENT_APP_CONFIG, { test: buildTsComponentAppWithDeclarationFalse } ); }); describe('ojet build --release', () => { - util.runComponentTestInAllTestApps({ test: buildComponentAppTest, component: [COMPONENT_NAME, COMPONENT_NAME_COMPOSITE, VCOMPONENT_NAME], release: true }); + util.runComponentTestInAllTestApps({ + test: buildComponentAppTest, + component: [COMPONENT_NAME, COMPONENT_NAME_COMPOSITE, VCOMPONENT_NAME], + release: true + }); }); describe('ojet build --release (bundle)', () => { - util.runComponentTestInAllTestApps({ test: buildReleaseExchangeComponentTest, pack: BUNDLE_TEST_EXCHANGE_COMPONENT}); + util.runComponentTestInAllTestApps({ + test: buildReleaseExchangeComponentTest, + pack: BUNDLE_TEST_EXCHANGE_COMPONENT + }); }); describe(`ojet build --${util.OMIT_COMPONENT_VERSION_FLAG}`, () => { - util.runComponentTestInAllTestApps({ test: omitComponentVerstionTest }); + util.runComponentTestInAllTestApps({ + test: omitComponentVerstionTest + }); }); describe('ojet build (component exists in both exchange and src folders)', () => { util.runComponentTestInAllTestApps({ @@ -805,33 +1251,62 @@ describe('Component & Jet Pack Tests', () => { // Which is unexpected since the component parameters are comp-1 (and vcomp-1), (so the .zip should not be pack-1.zip). // This should be investigated further. // - /* - describe('ojet remove component', () => { - util.runComponentTestInAllTestApps({ test: removeComponentTest, component: EXCHANGE_COMPONENT_NAME }); - }); - */ + /* + describe('ojet remove component', () => { + util.runComponentTestInAllTestApps({ test: removeComponentTest, component: EXCHANGE_COMPONENT_NAME }); + }); + */ }); describe('JET Pack Tests', () => { - function execPackCommand({ task, app, pack, version }) { + function execPackCommand({ + task, + app, + pack, + version + }) { return util.execCmd( - `${util.OJET_APP_COMMAND} ${task} pack ${pack}${version ? `@${version}` : ''}`, - { cwd: util.getAppDir(app) }, + `${util.OJET_APP_COMMAND} ${task} pack ${pack}${version ? `@${version}` : ''}`, { + cwd: util.getAppDir(app) + }, false, true ); } - - function execComponentInPackCommand({ task, app, pack, component, flags = '', squelch = false }) { + + function execComponentInPackCommand({ + task, + app, + pack, + component, + flags = '', + squelch = false + }) { const command = `${util.OJET_APP_COMMAND} ${task} component ${component} ${flags} --pack=${pack}`; - return util.execCmd(command, { cwd: util.getAppDir(app) }, squelch, true); + return util.execCmd(command, { + cwd: util.getAppDir(app) + }, squelch, true); } - - function beforePackTest({ task, app, pack, version, componentJson, scriptsFolder }) { + + function beforePackTest({ + task, + app, + pack, + version, + componentJson, + scriptsFolder + }) { before(async () => { - await execPackCommand({ task, app, pack, version }); + await execPackCommand({ + task, + app, + pack, + version + }); if (componentJson) { - const { pathToSourceComponents } = util.getAppPathData(app, scriptsFolder); + const { + pathToSourceComponents + } = util.getAppPathData(app, scriptsFolder); const pathToComponentComponentJson = path.join( pathToSourceComponents, pack, @@ -839,20 +1314,39 @@ describe('Component & Jet Pack Tests', () => { ); const componentComponentJson = fs.readJsonSync(pathToComponentComponentJson); fs.writeJsonSync( - pathToComponentComponentJson, - {...componentComponentJson, ...componentJson }, - { spaces: 2 } + pathToComponentComponentJson, { + ...componentComponentJson, + ...componentJson + }, { + spaces: 2 + } ); assert.ok(true); } }); } - - function beforeComponentInPackTest({ task, app, pack, component, flags = '', componentJson, scriptsFolder }) { + + function beforeComponentInPackTest({ + task, + app, + pack, + component, + flags = '', + componentJson, + scriptsFolder + }) { before(async () => { - await execComponentInPackCommand({ task, app, pack, component, flags }); + await execComponentInPackCommand({ + task, + app, + pack, + component, + flags + }); if (componentJson) { - const { pathToSourceComponents } = util.getAppPathData(app, scriptsFolder); + const { + pathToSourceComponents + } = util.getAppPathData(app, scriptsFolder); const pathToComponentComponentJson = path.join( pathToSourceComponents, pack, @@ -861,17 +1355,31 @@ describe('Component & Jet Pack Tests', () => { ); const componentComponentJson = fs.readJsonSync(pathToComponentComponentJson); fs.writeJsonSync( - pathToComponentComponentJson, - {...componentComponentJson, ...componentJson }, - { spaces: 2 } + pathToComponentComponentJson, { + ...componentComponentJson, + ...componentJson + }, { + spaces: 2 + } ); } }); } - - function createPackTest({ appName, scriptsFolder, pack, componentJson }) { + + function createPackTest({ + appName, + scriptsFolder, + pack, + componentJson + }) { if (!util.noScaffold()) { - beforePackTest({ task: 'create', app: appName, pack, componentJson, scriptsFolder }); + beforePackTest({ + task: 'create', + app: appName, + pack, + componentJson, + scriptsFolder + }); } describe('check created pack', () => { it(`should have ${appName}/src/${scriptsFolder}/${pack}/component.json`, () => { @@ -913,9 +1421,23 @@ describe('Component & Jet Pack Tests', () => { } }); } - function createComponentInPackTest({ appName, scriptsFolder, pack, component, componentJson }) { + + function createComponentInPackTest({ + appName, + scriptsFolder, + pack, + component, + componentJson + }) { if (!util.noScaffold()) { - beforeComponentInPackTest({ task: 'create', app: appName, pack, component, componentJson, scriptsFolder }); + beforeComponentInPackTest({ + task: 'create', + app: appName, + pack, + component, + componentJson, + scriptsFolder + }); } describe('check created component in pack', () => { it(`should have ${appName}/src/${scriptsFolder}/${pack}/${component}/component.json`, () => { @@ -959,7 +1481,7 @@ describe('Component & Jet Pack Tests', () => { }); }); } - + // // Create a 'stripped down' vcomponent in a pack. // The pack's vcomponent will have: @@ -969,9 +1491,20 @@ describe('Component & Jet Pack Tests', () => { // - missing dependencies from the pack's component.json. // // function createVComponentInPackTestNew({ appName, scriptsFolder, pack, component }) { - function createVComponentInPackTest({ appName, scriptsFolder, pack, component }) { + function createVComponentInPackTest({ + appName, + scriptsFolder, + pack, + component + }) { if (!util.noScaffold()) { - beforeComponentInPackTest({ task: 'create', app: appName, pack, component, flags: '--vcomponent' }); + beforeComponentInPackTest({ + task: 'create', + app: appName, + pack, + component, + flags: '--vcomponent' + }); } describe('check created component in pack', () => { const pathToComponent = util.getAppDir(path.join( @@ -989,7 +1522,9 @@ describe('Component & Jet Pack Tests', () => { }); it('should have the correct pack in @ojmetadata pack jsdoc', () => { const packRegex = new RegExp('@ojmetadata pack "(?.+)"'); - const componentContent = fs.readFileSync(pathToComponent, { encoding: 'utf-8' }); + const componentContent = fs.readFileSync(pathToComponent, { + encoding: 'utf-8' + }); const matches = packRegex.exec(componentContent); const packMatches = matches && matches.groups.pack === pack; assert(packMatches, 'vcomponent does not have correct pack @ojmetadata pack jsdoc'); @@ -1023,24 +1558,37 @@ describe('Component & Jet Pack Tests', () => { }); }); } - - function createResourceComponentInPackTest({ appName, pack, scriptsFolder, component, componentJson }) { + + function createResourceComponentInPackTest({ + appName, + pack, + scriptsFolder, + component, + componentJson + }) { if (!util.noScaffold()) { before(async () => { - const { pathToSourceComponents } = util.getAppPathData(appName, scriptsFolder); + const { + pathToSourceComponents + } = util.getAppPathData(appName, scriptsFolder); const packComponentPath = path.join(pathToSourceComponents, pack); const resourceComponentPath = path.join(packComponentPath, component); // Create resource component const resourceComponentCommand = `${util.OJET_APP_COMMAND} create component ${component} --pack=${pack} --type=resource`; - await util.execCmd(resourceComponentCommand, { cwd: util.getAppDir(appName) }, true, true); + await util.execCmd(resourceComponentCommand, { + cwd: util.getAppDir(appName) + }, true, true); // merge provided component.json object if available if (componentJson) { const resourceComponentComponentJsonPath = path.join(resourceComponentPath, util.COMPONENT_JSON); const resourceComponentComponentJson = fs.readJsonSync(resourceComponentComponentJsonPath); fs.writeJsonSync( - resourceComponentComponentJsonPath, - {...resourceComponentComponentJson, ...componentJson}, - { spaces: 2 } + resourceComponentComponentJsonPath, { + ...resourceComponentComponentJson, + ...componentJson + }, { + spaces: 2 + } ); } // Resource component is last to be created so can now set pack dependencies @@ -1048,53 +1596,105 @@ describe('Component & Jet Pack Tests', () => { const pathToPackComponentJson = path.join(packComponentPath, util.COMPONENT_JSON) const packComponentJson = fs.readJSONSync(pathToPackComponentJson); packComponentJson.dependencies = util.COMPONENT_JSON_DEPENDENCIES_TOKEN; - fs.writeJsonSync(pathToPackComponentJson, packComponentJson, { spaces: 2 }); + fs.writeJsonSync(pathToPackComponentJson, packComponentJson, { + spaces: 2 + }); }); } it('should check that resource component is in pack', () => { - const { pathToSourceComponents } = util.getAppPathData(appName, scriptsFolder); + const { + pathToSourceComponents + } = util.getAppPathData(appName, scriptsFolder); const packComponentPath = path.join(pathToSourceComponents, pack); const resourceComponentPath = path.join(packComponentPath, component); assert.ok(fs.existsSync(resourceComponentPath), `${component} not found in ${pack}`); }); it(`should check that resource component has correct component.json`, () => { - const { pathToSourceComponents } = util.getAppPathData(appName, scriptsFolder); + const { + pathToSourceComponents + } = util.getAppPathData(appName, scriptsFolder); const packComponentPath = path.join(pathToSourceComponents, pack); const resourceComponentPath = path.join(packComponentPath, component); if (fs.existsSync(resourceComponentPath)) { const componentJson = fs.readJsonSync(path.join(resourceComponentPath, 'component.json')); - assert.strictEqual(componentJson.name, component, `${component}'s name is not ${component} in component.json`); + assert.strictEqual(componentJson.name, component, `${component}'s name is not ${component} in component.json`); assert.strictEqual(componentJson.pack, pack, `${component}'s pack is not ${pack} in component.json`); assert.strictEqual(componentJson.type, 'resource', `${component}'s type is not resource in component.json}`); assert.ok(componentJson.dependencies, `${component}'s component.json does not have dependencies`); } }); + it(`should check that resource component has correct version after running ojet build --release`, async () => { + const { + pathToSourceComponents, + pathToBuiltComponents + } = util.getAppPathData(appName, scriptsFolder); + const packComponentJsonInSrcPath = path.join(pathToSourceComponents, pack, 'component.json'); + const packComponentJson = fs.readJsonSync(packComponentJsonInSrcPath); + const resourceComponentInSrcPath = path.join(pathToSourceComponents, pack, component); + const resourceComponentInWebPath = path.join(pathToBuiltComponents, pack, packComponentJson.version, component); + await util.execCmd(`${util.OJET_APP_COMMAND} build --release`, { + cwd: util.getAppDir(appName) + }, true); + if (fs.existsSync(resourceComponentInSrcPath) && fs.existsSync(resourceComponentInWebPath)) { + const errorMessage = `${component}'s version not the same in web's and src's component.json files`; + const componentJsonInSrc = fs.readJsonSync(path.join(resourceComponentInSrcPath, 'component.json')); + const componentJsonInWeb = fs.readJsonSync(path.join(resourceComponentInWebPath, 'component.json')); + assert.strictEqual(componentJsonInSrc.version, componentJsonInWeb.version, errorMessage); + } + }); it('should fail to create a resource component without a --pack flag', async () => { const resourceComponentCommand = `${util.OJET_APP_COMMAND} create component ${component} --type=resource`; - const result = await util.execCmd(resourceComponentCommand, { cwd: util.getAppDir(appName) }, true); + const result = await util.execCmd(resourceComponentCommand, { + cwd: util.getAppDir(appName) + }, true); assert.ok(/Cannot create resource component: please re-run the command with --pack and provide an existing JET pack/.test(result.stdout), result.stdout); }); it('should fail to create a resource component with an invalid pack name', async () => { const packNameTest = 'not-valid-pack-name'; const resourceComponentCommand = `${util.OJET_APP_COMMAND} create component ${component} --type=resource --pack=${packNameTest}`; - const result = await util.execCmd(resourceComponentCommand, { cwd: util.getAppDir(appName) }, true); + const result = await util.execCmd(resourceComponentCommand, { + cwd: util.getAppDir(appName) + }, true); assert.ok(/Invalid pack name: please provide an existing JET pack/.test(result.stdout), result.stdout); }); } - - function createComponentInPackFailureTest({ appName, pack, component }) { + + function createComponentInPackFailureTest({ + appName, + pack, + component + }) { describe('check create component in pack failure', () => { it('should fail with "Invalid pack name:"', async () => { const task = 'create'; - const result = await execComponentInPackCommand({ task, app: appName, pack, component, flags: '', squelch: true }); - assert.ok(util[`${task}ComponentInPackFailure`]({ stdout: result.stdout }), result.error); + const result = await execComponentInPackCommand({ + task, + app: appName, + pack, + component, + flags: '', + squelch: true + }); + assert.ok(util[`${task}ComponentInPackFailure`]({ + stdout: result.stdout + }), result.error); }); }); } - - function addPackTest({ appName, scriptsFolder, pack, version }) { + + function addPackTest({ + appName, + scriptsFolder, + pack, + version + }) { if (!util.noScaffold()) { - beforePackTest({ task: 'add', app: appName, pack, version }); + beforePackTest({ + task: 'add', + app: appName, + pack, + version + }); } describe('check added pack', () => { it(`should have ${appName}/jet_components/${pack}/component.json`, () => { @@ -1140,8 +1740,16 @@ describe('Component & Jet Pack Tests', () => { }); }); } - - function buildComponentAppTest({ appName, pack, component, vcomponent, resourceComponent, release, scriptsFolder }) { + + function buildComponentAppTest({ + appName, + pack, + component, + vcomponent, + resourceComponent, + release, + scriptsFolder + }) { const testName = release ? 'Build (Release)' : 'Build'; const buildType = release ? 'release' : 'default'; describe(testName, () => { @@ -1150,7 +1758,9 @@ describe('Component & Jet Pack Tests', () => { it(`should build ${buildType} component app`, async () => { let command = `${util.OJET_APP_COMMAND} build`; command = release ? `${command} --release` : command; - const result = await util.execCmd(command, { cwd: appDir }, false, true); + const result = await util.execCmd(command, { + cwd: appDir + }, false, true); assert.equal(util.buildSuccess(result.stdout), true, result.error); }); } @@ -1161,16 +1771,32 @@ describe('Component & Jet Pack Tests', () => { it('should have pack(s) directory', () => { assert.ok(fs.existsSync(packDir)); }) - packMemberExistenceTest({ packDir, component }); - packMemberExistenceTest({ packDir, component: resourceComponent, type: 'resource' }); + packMemberExistenceTest({ + packDir, + component + }); + packMemberExistenceTest({ + packDir, + component: resourceComponent, + type: 'resource' + }); if (release) { // Test for minified pack and pack components for release build it('should have pack(s) with /min directory', () => { const exists = fs.pathExistsSync(packMinDir); assert.ok(exists, packMinDir); }); - packMemberExistenceTest({ packDir: packMinDir, component, release: true }); - packMemberExistenceTest({ packDir: packMinDir, component: resourceComponent, release: true, type: 'resource' }); + packMemberExistenceTest({ + packDir: packMinDir, + component, + release: true + }); + packMemberExistenceTest({ + packDir: packMinDir, + component: resourceComponent, + release: true, + type: 'resource' + }); } else { // Test for missing min/ for debug build it('should not have pack(s) with /min directory', () => { @@ -1180,18 +1806,29 @@ describe('Component & Jet Pack Tests', () => { } if (scriptsFolder === 'ts') { // Test for type definitions and vcomponent in ts app - packMemberExistenceTest({ packDir, component: vcomponent }); + packMemberExistenceTest({ + packDir, + component: vcomponent + }); if (release) { - packMemberExistenceTest({ packDir: packMinDir, component: vcomponent, release: true }); + packMemberExistenceTest({ + packDir: packMinDir, + component: vcomponent, + release: true + }); } it('should have pack(s) with /types directory', () => { - const { pathToBuiltComponents } = util.getAppPathData(appName) + const { + pathToBuiltComponents + } = util.getAppPathData(appName) const typesDir = path.join(pathToBuiltComponents, pack, DEFAULT_PACK_VERSION, 'types', vcomponent); const exists = fs.pathExistsSync(typesDir); assert.ok(exists, typesDir); }); it('should not have vcomponent(s) in pack with /types directory', () => { - const { pathToBuiltComponents } = util.getAppPathData(appName) + const { + pathToBuiltComponents + } = util.getAppPathData(appName) const typesDir = path.join(pathToBuiltComponents, pack, DEFAULT_PACK_VERSION, vcomponent, 'types'); const exists = fs.pathExistsSync(typesDir); assert.ok(!exists, typesDir); @@ -1200,7 +1837,11 @@ describe('Component & Jet Pack Tests', () => { }); } - function packMemberExistenceTest({ packDir, component, type = 'composite' }) { + function packMemberExistenceTest({ + packDir, + component, + type = 'composite' + }) { it(`should have pack(s) with ${path.basename(packDir)}/${component}`, () => { assert.ok(fs.existsSync(path.join(packDir, component)), `Built pack does not have ${packDir}/${component}/${component}`); }); @@ -1220,54 +1861,88 @@ describe('Component & Jet Pack Tests', () => { }); } } - - function stripMetadatainMinLoaderPackTest({ appName, scriptsFolder, component, pack}) { + + function stripMetadatainMinLoaderPackTest({ + appName, + scriptsFolder, + component, + pack + }) { if (!util.noScaffold()) { - beforePackTest({ task: 'create', app: appName, pack, componentJson: STRIP_TEST_COMPONENT_JSON, scriptsFolder }); - beforeComponentInPackTest({ task: 'create', app: appName, pack, component, componentJson: STRIP_TEST_COMPONENT_JSON, scriptsFolder }); + beforePackTest({ + task: 'create', + app: appName, + pack, + componentJson: STRIP_TEST_COMPONENT_JSON, + scriptsFolder + }); + beforeComponentInPackTest({ + task: 'create', + app: appName, + pack, + component, + componentJson: STRIP_TEST_COMPONENT_JSON, + scriptsFolder + }); } describe('check that stripped metadata is in min/loader.js but not in staging', () => { - it('should have stripped metadata in min/loader.js', async() => { + it('should have stripped metadata in min/loader.js', async () => { const appDir = util.getAppDir(appName); - const {pathToSourceComponents, pathToBuiltComponents} = util.getAppPathData(appName, scriptsFolder); + const { + pathToSourceComponents, + pathToBuiltComponents + } = util.getAppPathData(appName, scriptsFolder); const pathToLocalComponentInWeb = path.join(pathToBuiltComponents, pack, DEFAULT_COMPONENT_VERSION, component, 'component.json'); const pathToLocalComponentInSrc = path.join(pathToSourceComponents, pack, component, 'component.json'); const componentMinDir = pathToLocalComponentInWeb.replace(`${component}${path.sep}component.json`, COMPOSITE_COMPONENT_OPTIMIZED_FOLDER); const componentMinLoader = path.join(componentMinDir, component, COMPOSITE_COMPONENT_OPTIMIZED_FILE); // The required list of required attributes at run-time is: 'properties', 'methods', 'events', 'slots', and 'dynamicSlots'. // Since other attributes are pre defined, then we will be adding only dynamicSlots which is not common: - const result = await util.execCmd(`${util.OJET_APP_COMMAND} build --release`, { cwd: appDir}, true, true); + const result = await util.execCmd(`${util.OJET_APP_COMMAND} build --release`, { + cwd: appDir + }, true, true); assert.equal(util.buildSuccess(result.stdout), true, result.error); // Get the test variables to check: - const { - webHasAllComponentJSONSrcAttributes, - errorMessageForWeb, - loaderHasStrippedAttributes, - errorMessageForLoader - } = getStripTestVariables({pathToLocalComponentInWeb, pathToLocalComponentInSrc, componentMinLoader}); + const { + webHasAllComponentJSONSrcAttributes, + errorMessageForWeb, + loaderHasStrippedAttributes, + errorMessageForLoader + } = getStripTestVariables({ + pathToLocalComponentInWeb, + pathToLocalComponentInSrc, + componentMinLoader + }); // Delete the created components in the src and exchange folders: fs.removeSync(path.join(pathToSourceComponents, pack)); // Check the results: assert.equal(loaderHasStrippedAttributes, true, errorMessageForLoader); assert.equal(webHasAllComponentJSONSrcAttributes, true, errorMessageForWeb); }); - }); + }); } - function buildReleaseCheckBundle({ appName, pack }) { - const testName ='Build (Release)'; + function buildReleaseCheckBundle({ + appName, + pack + }) { + const testName = 'Build (Release)'; const buildType = 'release'; - describe(testName, () => { + describe(testName, () => { if (!util.noBuild()) { it(`should build ${buildType} component app`, async () => { const appDir = util.getAppDir(appName); let command = `${util.OJET_APP_COMMAND} build --release`; - const result = await util.execCmd(command, { cwd: appDir }, false, true); + const result = await util.execCmd(command, { + cwd: appDir + }, false, true); assert.equal(util.buildSuccess(result.stdout), true, result.error); }); } it('should not have the bundle stub file', () => { - const { pathToBuiltComponents } = util.getAppPathData(appName) + const { + pathToBuiltComponents + } = util.getAppPathData(appName) const pathToBundle = path.join( pathToBuiltComponents, pack, @@ -1277,40 +1952,48 @@ describe('Component & Jet Pack Tests', () => { const exists = fs.existsSync(pathToBundle); assert.ok(!exists, pathToBundle); }); - + it('release build: path mapping to minified bundle', async () => { // The following entry should be in paths: // "packbundle-1":"jet-composites/packbundle-1/1.0.0/min" - const bundleContent = getBundleJsContent({ appName }); - assert.equal(bundleContent.toString().match(`jet-composites/${BUNDLE_PACK_NAME}/1.0.0/min`), - `jet-composites/${BUNDLE_PACK_NAME}/1.0.0/min`, - `bundle.js should contain the minified bundle ${BUNDLE_PACK_NAME}`); - + const bundleContent = getBundleJsContent({ + appName + }); + assert.equal(bundleContent.toString().match(`jet-composites/${BUNDLE_PACK_NAME}/1.0.0/min`), + `jet-composites/${BUNDLE_PACK_NAME}/1.0.0/min`, + `bundle.js should contain the minified bundle ${BUNDLE_PACK_NAME}`); + }); - + it('release build: bundle content (local component)', async () => { // Check the bundle for the local pack bundle property const localPackBundle = `${BUNDLE_PACK_NAME}/${BUNDLE_NAME}`; var hasLocalPackBundle = false; - const bundlesPropObj = getBundleJsBundlesObject({ appName }); + const bundlesPropObj = getBundleJsBundlesObject({ + appName + }); if (bundlesPropObj.hasOwnProperty(localPackBundle)) { hasLocalPackBundle = true; } assert.ok(hasLocalPackBundle, `local pack bundle ${localPackBundle} injected into bundles.js`); }); - + it('release build: bundle content (exchange component)', async () => { // Check the bundle for the exchange pack bundle property const exchangePackBundle = `${EXCHANGE_PACK}/${EXCHANGE_PACK_BUNDLE}`; var hasExchangePackBundle = false; - const bundlesPropObj = getBundleJsBundlesObject({ appName }); + const bundlesPropObj = getBundleJsBundlesObject({ + appName + }); if (bundlesPropObj.hasOwnProperty(exchangePackBundle)) { hasExchangePackBundle = true; } assert.ok(hasExchangePackBundle, `exchange pack bundle ${exchangePackBundle} injected into bundles.js`); }); it(`should create pack bundle containing only ${BUNDLE_COMPONENT_NAME1} & ${RESOURCE_COMPONENT_NAME}`, () => { - const { pathToBuiltComponents } = util.getAppPathData(appName); + const { + pathToBuiltComponents + } = util.getAppPathData(appName); const pathToPackRoot = path.join( pathToBuiltComponents, pack, @@ -1336,26 +2019,39 @@ describe('Component & Jet Pack Tests', () => { const expectedComponentsInBundle = new Set(componentsInBundle); // the goal of this test is to read the bundle contents and ensure that the only components in // the bundle are the ones listed in the pack's bundle configuration - const packBundleContent = fs.readFileSync(pathToPackBundle, { encoding: 'utf-8' }); + const packBundleContent = fs.readFileSync(pathToPackBundle, { + encoding: 'utf-8' + }); // this regex extracts from define('//',...) lines in the bundle const componentRegex = new RegExp(`${pack}\/(?[\\w-]+)\/`, 'gi'); let regexMatch; // loop through all components and verify that they should be in the bundle // (i.e. in // definitions) while ((regexMatch = componentRegex.exec(packBundleContent)) !== null) { - const { groups: { component } } = regexMatch; + const { + groups: { + component + } + } = regexMatch; assert.ok( - expectedComponentsInBundle.has(component), + expectedComponentsInBundle.has(component), `${pack}-${component} is in ${BUNDLE_NAME} but is not listed as a bundle member` - ); + ); } }); }); } - - function packagePackTest({ appName, pack }) { + + function packagePackTest({ + appName, + pack + }) { if (!util.noScaffold()) { - beforePackTest({ task: 'package', app: appName, pack }); + beforePackTest({ + task: 'package', + app: appName, + pack + }); } describe('check packaged pack', () => { it(`should be packaged in ${appName}/dist/${pack}.zip`, () => { @@ -1370,20 +2066,44 @@ describe('Component & Jet Pack Tests', () => { }); } - function packagePackHookTest({ appName, component, pack, scriptsFolder }) { + function packagePackHookTest({ + appName, + component, + pack, + scriptsFolder + }) { describe('check that packs are packaged through the hooks successfully', () => { - it('should package the pack through the hooks', async() => { + it('should package the pack through the hooks', async () => { const appDir = util.getAppDir(appName); - const {beforePackageHookPath, afterPackageHookPath, defaultBeforeHookContent, defaultAfterHookContent} = util.getHooksPathAndContent(appName); + const { + beforePackageHookPath, + afterPackageHookPath, + defaultBeforeHookContent, + defaultAfterHookContent + } = util.getHooksPathAndContent(appName); // write custom hooks file content for testing: - util.writeCustomHookContents({hookName:'before', filePath: beforePackageHookPath}); - util.writeCustomHookContents({hookName:'after', filePath: afterPackageHookPath}); + util.writeCustomHookContents({ + hookName: 'before', + filePath: beforePackageHookPath + }); + util.writeCustomHookContents({ + hookName: 'after', + filePath: afterPackageHookPath + }); // create the component and pack to package: - await util.execCmd(`${util.OJET_APP_COMMAND} create pack ${pack}`, { cwd: appDir }, true, true); - await util.execCmd(`${util.OJET_APP_COMMAND} create component ${component} --pack=${pack}`, { cwd: appDir}, true, true); - const result = await util.execCmd(`${util.OJET_APP_COMMAND} package pack ${pack}`, { cwd: appDir }, true, true); + await util.execCmd(`${util.OJET_APP_COMMAND} create pack ${pack}`, { + cwd: appDir + }, true, true); + await util.execCmd(`${util.OJET_APP_COMMAND} create component ${component} --pack=${pack}`, { + cwd: appDir + }, true, true); + const result = await util.execCmd(`${util.OJET_APP_COMMAND} package pack ${pack}`, { + cwd: appDir + }, true, true); // Delete created component - const { pathToSourceComponents } = util.getAppPathData(appName, scriptsFolder); + const { + pathToSourceComponents + } = util.getAppPathData(appName, scriptsFolder); fs.removeSync(path.join(pathToSourceComponents, component)); // Revert default hook content fs.writeFileSync(beforePackageHookPath, defaultBeforeHookContent); @@ -1392,12 +2112,21 @@ describe('Component & Jet Pack Tests', () => { assert.ok(/Running before_component_package for component: component being packaged is package-hooks-pack/.test(result.stdout), result.stdout); assert.ok(/Running after_component_package for component: component being packaged is package-hooks-pack/.test(result.stdout), result.stdout); }); - }); + }); } - - function buildPackTest({ appName, pack, vcomponent, scriptsFolder }) { + + function buildPackTest({ + appName, + pack, + vcomponent, + scriptsFolder + }) { if (!util.noScaffold()) { - beforePackTest({ task: 'build', app: appName, pack }); + beforePackTest({ + task: 'build', + app: appName, + pack + }); } describe('check built pack', () => { const appDir = util.getAppDir(appName); @@ -1433,17 +2162,25 @@ describe('Component & Jet Pack Tests', () => { }) } - function getBundleJsContent({ appName }) { - const{ pathToBundleJs } = util.getAppPathData(appName); + function getBundleJsContent({ + appName + }) { + const { + pathToBundleJs + } = util.getAppPathData(appName); const bundleContent = fs.readFileSync(pathToBundleJs); return bundleContent; } - + // // Return a bundle property object. // - function getBundleJsBundlesObject({ appName }) { - const bundleContent = getBundleJsContent({ appName }); + function getBundleJsBundlesObject({ + appName + }) { + const bundleContent = getBundleJsContent({ + appName + }); // // Read the bundle properties from bundleContent, // then convert these bundle property/value arrays to an object. @@ -1454,31 +2191,42 @@ describe('Component & Jet Pack Tests', () => { // - exchange pack bundle // For the exchange pack bundle, we just check the prefix // - + // Regular expression to extract all bundle properties. // Used to parse bundles.js let bundlesRegEx = /bundles:\{([^{}]*)\}/; - + // extract bundles properties const bundlesProps = bundleContent.toString().match(bundlesRegEx).pop(); - + // Add brackets to form object var bundlesPropsPlusBrackets = `\{${bundlesProps}\}`; // convert to object var bundlesPropObj = JSON.parse(bundlesPropsPlusBrackets); - + return bundlesPropObj; - } + } describe('ojet create pack', () => { - util.runComponentTestInAllTestApps({ test: createPackTest, pack: PACK_NAME }); + util.runComponentTestInAllTestApps({ + test: createPackTest, + pack: PACK_NAME + }); }); describe('ojet create component --pack', () => { describe('valid pack name', () => { - util.runComponentTestInAllTestApps({ test: createComponentInPackTest, pack: PACK_NAME, component: COMPONENT_NAME }); + util.runComponentTestInAllTestApps({ + test: createComponentInPackTest, + pack: PACK_NAME, + component: COMPONENT_NAME + }); }) describe('invalid pack name', () => { - util.runComponentTestInAllTestApps({ test: createComponentInPackFailureTest, pack: 'pack-2', component: COMPONENT_NAME }); + util.runComponentTestInAllTestApps({ + test: createComponentInPackFailureTest, + pack: 'pack-2', + component: COMPONENT_NAME + }); }) }); // @@ -1487,8 +2235,7 @@ describe('Component & Jet Pack Tests', () => { // describe('ojet create component --vcomponent --pack', () => { util.runComponentTestInTestApp( - util.TYPESCRIPT_COMPONENT_APP_CONFIG, - { + util.TYPESCRIPT_COMPONENT_APP_CONFIG, { test: createVComponentInPackTest, pack: PACK_NAME, component: VCOMPONENT_NAME @@ -1497,7 +2244,7 @@ describe('Component & Jet Pack Tests', () => { }); describe('create resource component', () => { util.runComponentTestInAllTestApps({ - test: createResourceComponentInPackTest, + test: createResourceComponentInPackTest, pack: PACK_NAME, component: RESOURCE_COMPONENT_NAME, componentJson: { @@ -1508,17 +2255,20 @@ describe('Component & Jet Pack Tests', () => { }); }); describe('ojet add pack', () => { - util.runComponentTestInAllTestApps({ test: addPackTest, pack: EXCHANGE_PACK, version: EXCHANGE_PACK_VERSION }); + util.runComponentTestInAllTestApps({ + test: addPackTest, + pack: EXCHANGE_PACK, + version: EXCHANGE_PACK_VERSION + }); }) describe('ojet package pack', () => { util.runComponentTestInAllTestApps({ - test: packagePackTest, + test: packagePackTest, pack: PACK_NAME }); util.runComponentTestInTestApp( - util.TYPESCRIPT_COMPONENT_APP_CONFIG, - { - test: packagePackTest, + util.TYPESCRIPT_COMPONENT_APP_CONFIG, { + test: packagePackTest, pack: PACK_NAME, component: VCOMPONENT_NAME } @@ -1528,24 +2278,38 @@ describe('Component & Jet Pack Tests', () => { describe('ojet package pack (hook test)', () => { util.runComponentTestInAllTestApps({ test: packagePackHookTest, - component: 'package-hooks-component', + component: 'package-hooks-component', pack: 'package-hooks-pack' }); }); - + describe('ojet build', () => { - util.runComponentTestInAllTestApps({ test: buildComponentAppTest, pack: PACK_NAME, component: COMPONENT_NAME, vcomponent: VCOMPONENT_NAME, resourceComponent: RESOURCE_COMPONENT_NAME, release: false }); + util.runComponentTestInAllTestApps({ + test: buildComponentAppTest, + pack: PACK_NAME, + component: COMPONENT_NAME, + vcomponent: VCOMPONENT_NAME, + resourceComponent: RESOURCE_COMPONENT_NAME, + release: false + }); }); + describe('ojet build --release', () => { - util.runComponentTestInAllTestApps({ test: buildComponentAppTest, pack: PACK_NAME, component: COMPONENT_NAME, vcomponent: VCOMPONENT_NAME, resourceComponent: RESOURCE_COMPONENT_NAME, release: true }); + util.runComponentTestInAllTestApps({ + test: buildComponentAppTest, + pack: PACK_NAME, + component: COMPONENT_NAME, + vcomponent: VCOMPONENT_NAME, + resourceComponent: RESOURCE_COMPONENT_NAME, + release: true + }); }); // Verify the 'stripped down vcomponent' created in createVComponentInPackTest. describe('ojet build pack ', () => { util.runComponentTestInTestApp( - util.TYPESCRIPT_COMPONENT_APP_CONFIG, - { - test: buildPackTest, + util.TYPESCRIPT_COMPONENT_APP_CONFIG, { + test: buildPackTest, pack: PACK_NAME, vcomponent: VCOMPONENT_NAME } @@ -1582,7 +2346,7 @@ describe('Component & Jet Pack Tests', () => { // describe('ojet create pack (bundle) ', () => { util.runComponentTestInAllTestApps({ - test: createPackTest, + test: createPackTest, pack: BUNDLE_PACK_NAME, componentJson: { bundles: { @@ -1619,16 +2383,14 @@ describe('Component & Jet Pack Tests', () => { // create two pack vcomponents describe('ojet create component --vcomponent --pack (bundle) ', () => { util.runComponentTestInTestApp( - util.TYPESCRIPT_COMPONENT_APP_CONFIG, - { + util.TYPESCRIPT_COMPONENT_APP_CONFIG, { test: createVComponentInPackTest, pack: BUNDLE_PACK_NAME, component: BUNDLE_VCOMPONENT_NAME1 } ); util.runComponentTestInTestApp( - util.TYPESCRIPT_COMPONENT_APP_CONFIG, - { + util.TYPESCRIPT_COMPONENT_APP_CONFIG, { test: createVComponentInPackTest, pack: BUNDLE_PACK_NAME, component: BUNDLE_VCOMPONENT_NAME2 @@ -1638,7 +2400,7 @@ describe('Component & Jet Pack Tests', () => { describe('create resource component (bundle)', () => { util.runComponentTestInAllTestApps({ - test: createResourceComponentInPackTest, + test: createResourceComponentInPackTest, pack: BUNDLE_PACK_NAME, component: RESOURCE_COMPONENT_NAME, componentJson: { @@ -1651,7 +2413,7 @@ describe('Component & Jet Pack Tests', () => { describe('ojet build pack ', () => { util.runComponentTestInAllTestApps({ - test: buildPackTest, + test: buildPackTest, pack: BUNDLE_PACK_NAME, vcomponent: BUNDLE_VCOMPONENT_NAME1 }); @@ -1681,7 +2443,7 @@ describe('Component & Jet Pack Tests', () => { util.runComponentTestInAllTestApps({ test: stripMetadatainMinLoaderPackTest, pack: STRIP_TEST_PACK_NAME, - component: STRIP_TEST_COMPONENT_NAME + component: STRIP_TEST_COMPONENT_NAME }); }); }); diff --git a/test/hybridTest.js b/test/hybridTest.js index 01465ac..2ac31c5 100644 --- a/test/hybridTest.js +++ b/test/hybridTest.js @@ -132,7 +132,7 @@ if (!util.noHybrid()) { } it('should not have \'locale_\' dirs in resources', () => { - const resourcePath = path.resolve(hybridTestDir, 'www/js/libs/oj', `v${util.getJetVersion(util.HYBRID_APP_NAME)}`, 'resources/nls'); + const resourcePath = path.resolve(hybridTestDir, 'www/js/libs/oj', `${util.getJetVersion(util.HYBRID_APP_NAME)}`, 'resources/nls'); const locList = fs.readdirSync(resourcePath); if (locList) { locList.forEach((elem) => { @@ -180,6 +180,20 @@ if (!util.noHybrid()) { }); } + /* describe('add pcss', () => { + it('should add pcss generator', async () => { + const result = await util.execCmd(`${util.OJET_APP_COMMAND} add theming`, { cwd: util.getAppDir(util.HYBRID_APP_NAME) }); + assert.equal(/add pcss complete/.test(result.stdout), true, result.stdout); + }); + }); + + describe('create theme', () => { + it('should add green theme', async () => { + const result = await util.execCmd(`${util.OJET_APP_COMMAND} create theme green --basetheme=redwood`, { cwd: util.getAppDir(util.HYBRID_APP_NAME), maxBuffer: 1024 * 20000 }); + assert.equal(/green theme added/.test(result.stdout), true, result.error); + }); + }); */ + if (!util.noCordova()) { describe('Plugin management', async () => { const batteryPlugin = 'cordova-plugin-battery-status'; diff --git a/test/ojcTest.js b/test/ojcTest.js new file mode 100644 index 0000000..eda46d7 --- /dev/null +++ b/test/ojcTest.js @@ -0,0 +1,106 @@ +/** + Copyright (c) 2015, 2022, Oracle and/or its affiliates. + Licensed under The Universal Permissive License (UPL), Version 1.0 + as shown at https://oss.oracle.com/licenses/upl/ + +*/ +const assert = require('assert'); +const fs = require('fs-extra'); +const path = require('path'); + +const util = require('./util'); + +let filelist; +const appDir = util.getAppDir(util.OJC_APP_NAME); + +describe('oj-c App Test', () => { + before(async () => { + if (!util.noScaffold()) { + util.removeAppDir(util.OJC_APP_NAME); + + // Scaffold an oj-c app + let result = await util.execCmd(`${util.OJET_COMMAND} create ${util.OJC_APP_NAME} --use-global-tooling --typescript`, { cwd: util.testDir }); + console.log(result.stdout); + // Check that it output the right text to the command line + assert.strictEqual(util.norestoreSuccess(result.stdout), true, result.stderr); + } + }); + + describe('Check essential files', () => { + it('should have package.json', () => { + filelist = fs.readdirSync(appDir); + const inlist = filelist.indexOf('package.json') > -1; + assert.equal(inlist, true, `${path.resolve(appDir, 'package.json')} missing`); + }); + + it('should have .gitignore', () => { + filelist = fs.readdirSync(appDir); + const inlist = filelist.indexOf('.gitignore') > -1; + assert.equal(inlist, true, `${path.resolve(appDir, '.gitignore')} missing`); + }); + }); + + describe('Build', () => { + if (!util.noBuild()) { + it('should build default js app', async () => { + const result = await util.execCmd(`${util.OJET_APP_COMMAND} build web`, { cwd: appDir }); + assert.equal(util.buildSuccess(result.stdout), true, result.error); + }); + } + }); + + +describe('Build with cdn', () => { + const pathToPathMappingJson = path.join(appDir, 'src', 'js', 'path_mapping.json'); + before(() => { + // Change "use" to "cdn" in path_mapping.json + const pathmappingJson = fs.readJsonSync(pathToPathMappingJson); + pathmappingJson.use = 'cdn'; + // write it back out + fs.writeJsonSync(pathToPathMappingJson, pathmappingJson); + }); + after(() => { + // Revert "use" to "local" in path_mapping.json + const pathmappingJson = fs.readJsonSync(pathToPathMappingJson); + pathmappingJson.use = 'local'; + // write it back out + fs.writeJsonSync(pathToPathMappingJson, pathmappingJson); + }); + function sharedTests({ debug }) { + const pathToMainjs = debug ? path.join(appDir, 'web', 'js', 'main.js') : path.join(appDir, 'web', 'js', 'bundle.js'); + it('should build main.js with a reference to oj-c on the cdn', () => { + const mainJs = fs.readFileSync(pathToMainjs, { encoding: 'utf-8' }); + const pathString = '../../packs/oj-c'; + assert.ok(new RegExp(pathString).test(mainJs), 'main.js should contain a reference to packs/oj-c on the cdn '); + }); + } + describe('debug build', () => { + if (!util.noBuild()) { + it('should build in debug mode to reference the CDN', async () => { + const result = await util.execCmd(`${util.OJET_APP_COMMAND} build web`, { cwd: appDir }); + assert.equal(util.buildSuccess(result.stdout), true, result.error); + }); + } + sharedTests({ debug: true }); + }); + describe('release build', () => { + if (!util.noBuild()) { + it('should build in release mode to reference the CDN', async () => { + const result = await util.execCmd(`${util.OJET_APP_COMMAND} build web --release`, { cwd: appDir }); + assert.equal(util.buildSuccess(result.stdout), true, result.error); + }); + } + sharedTests({ debug: false }); + it('should delete libraries referenced from the cdn', () => { + const libsWhitelist = ['oj', 'require', 'require-css']; + let hasLibNotInWhitelist = false; + fs.readdirSync(path.join(appDir, 'web', 'js', 'libs')).forEach((lib) => { + if (!libsWhitelist.includes(lib)) { + hasLibNotInWhitelist = true; + } + }); + assert.ok(!hasLibNotInWhitelist, 'libs should not contain libraries referenced from the cdn'); + }); + }); + }); +}); diff --git a/test/templates/webTsApiTest/package.json b/test/templates/webTsApiTest/package.json index 727ebb0..079981c 100644 --- a/test/templates/webTsApiTest/package.json +++ b/test/templates/webTsApiTest/package.json @@ -3,14 +3,16 @@ "version": "1.0.0", "description": "An Oracle JavaScript Extension Toolkit(JET) web app", "dependencies": { - "@oracle/oraclejet": "https://artifacthub-phx.oci.oraclecorp.com/ojet-dev-local/oracle-oraclejet-12.1.0.tgz" + "@oracle/oraclejet": "https://artifacthub-phx.oci.oraclecorp.com/ojet-dev-local/oracle-oraclejet-13.0.0.tgz" }, "devDependencies": { "@oracle/oraclejet-tooling":"file:../../ojet-cli/node_modules/@oracle/oraclejet-tooling", "fs-extra": "^8.1.0", - "glob": "^7.1.1", + "glob": "7.2.0", "underscore": "^1.10.2", - "typescript": "4.5.4" + "ts-creator": "~1.2.5", + "yargs-parser": "~13.1.2", + "typescript": "4.6.4" }, "engines": { "node": ">=12.21.0" diff --git a/test/templates/webTsApiTest/src/css/demo-alta-site-min.css b/test/templates/webTsApiTest/src/css/demo-alta-site-min.css index 22b2016..efd1461 100644 --- a/test/templates/webTsApiTest/src/css/demo-alta-site-min.css +++ b/test/templates/webTsApiTest/src/css/demo-alta-site-min.css @@ -1 +1 @@ -@font-face{font-family:'AppFont';src:url("fonts/App_iconfont.woff") format("woff");font-weight:normal;font-style:normal}.demo-icon-font,.demo-icon-font-24{font-family:AppFont;font-size:16px;line-height:1;display:inline-block;font-weight:normal;speak:none;font-style:normal;font-variant:normal;text-transform:none;text-align:center;box-sizing:content-box;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.demo-icon-font:before,.demo-icon-font-24:before{display:inline-block}.demo-icon-font-24{font-size:24px}.demo-bookmark-icon-16:before{content:"\e606"}.demo-grid-icon-16:before{content:"\e607"}.demo-list-icon-16:before{content:"\e608"}.demo-gear-icon-16:before{content:"\e609"}.demo-filter-icon-16:before{content:"\e907"}.demo-share-icon-16:before{content:"\e90a"}.demo-home-icon-24:before{content:"\e600"}.demo-education-icon-24:before{content:"\e601"}.demo-catalog-icon-24:before{content:"\e602"}.demo-palette-icon-24:before{content:"\e603"}.demo-library-icon-24:before{content:"\e604"}.demo-chat-icon-24:before{content:"\e605"}.demo-edit-icon-24:before{content:"\e906"}.demo-fire-icon-24:before{content:"\e908"}.demo-person-icon-24:before{content:"\e909"}.demo-signout-icon-24:before{content:"\e90b"}.demo-info-icon-24:before{content:"\e900"}.demo-copy-icon-24:before{content:"\e901"}.demo-people-icon-24:before{content:"\e902"}.demo-chart-icon-24:before{content:"\e903"}.demo-garbage-icon-24:before{content:"\e904"}.demo-download-icon-24:before{content:"\e905"}.demo-oracle-icon{width:137px;height:18px}.demo-oracle-icon:before{content:url("images/oracle_logo.svg")}html:not([dir="rtl"]) .demo-oracle-icon{padding-right:4px}html[dir="rtl"] .demo-oracle-icon{padding-left:4px}.demo-oracle-icon:before{display:inline}.demo-appheader{background-color:#f0f0f0;box-shadow:0 3px 3px rgba(0,0,0,0.1);position:relative;margin-bottom:1px}.oj-slow-boxshadow .demo-appheader{border-bottom-color:#d9dfe3}.demo-appheader-appname{font-size:1.143rem;font-weight:normal;color:#4d4d4d}html:not([dir="rtl"]) .demo-appheader-toolbar-container{text-align:right}html[dir="rtl"] .demo-appheader-toolbar-container{text-align:left}.demo-appheader-toolbar-container .oj-toolbar{display:inline-block;margin-right:0;margin-left:0}.demo-appheader-logo-block{display:inline-block}.demo-appheader-offcanvas-pushwrapper{width:100%;overflow-x:hidden}.demo-appheader-appname-block{vertical-align:2px}html:not([dir="rtl"]) .demo-appheader-appname-block{margin-left:4px}html[dir="rtl"] .demo-appheader-appname-block{margin-right:4px}.demo-offcanvas-toggle{display:block}html:not([dir="rtl"]) .demo-offcanvas-toggle{float:left}html[dir="rtl"] .demo-offcanvas-toggle{float:right}.demo-appheader-classic .demo-appheader-nav{display:none}@media screen and (max-width: 767px){.demo-appheader{padding-left:2px;padding-right:2px}.demo-appheader-appname-block,.demo-appheader-pulldown-normal,.demo-appheader-toolbar-separator,.demo-appheader-toolbar-button{display:none}.demo-appheader-pulldown-small{display:inline-block}.demo-appheader-classic .demo-offcanvas-toggle{width:30%;position:relative}.demo-appheader-classic .demo-appheader-logo{text-align:center;float:none;width:40%;position:relative}html:not([dir="rtl"]) .demo-appheader-classic .demo-appheader-logo{margin-left:0}html[dir="rtl"] .demo-appheader-classic .demo-appheader-logo{margin-right:0}}@media print, screen and (min-width: 768px){html:not([dir="rtl"]) .demo-offcanvas-toggle{margin-left:-10px}html[dir="rtl"] .demo-offcanvas-toggle{margin-right:-10px}.demo-appheader-appname-block,.demo-appheader-pulldown-normal{display:inline-block}.demo-appheader-pulldown-small{display:none}.demo-appheader{padding-left:10px;padding-right:10px}}@media print and (orientation: landscape), screen and (min-width: 1024px){.demo-offcanvas-toggle{display:none}.demo-appheader-appname-block{display:inline-block}html:not([dir="rtl"]) .demo-appheader-classic .demo-appheader-logo{margin-left:0}html[dir="rtl"] .demo-appheader-classic .demo-appheader-logo{margin-right:0}.demo-appheader-classic .demo-appheader-nav{display:block}html:not([dir="rtl"]) .demo-appheader-classic .demo-appheader-nav{text-align:right}html[dir="rtl"] .demo-appheader-classic .demo-appheader-nav{text-align:left}}.demo-appheader-lite .demo-appheader-logo{margin-top:9px}html:not([dir="rtl"]) .demo-appheader-lite .demo-appheader-logo{float:left}html[dir="rtl"] .demo-appheader-lite .demo-appheader-logo{float:right}.demo-appheader-lite .oj-toolbar{margin-top:0}.demo-appheader-classic .demo-appheader-logo{margin-top:8px}html:not([dir="rtl"]) .demo-appheader-classic .demo-appheader-logo{float:left;margin-left:4px}html[dir="rtl"] .demo-appheader-classic .demo-appheader-logo{float:right;margin-right:4px}.demo-appheader-logo-oracle{padding-top:3px}.demo-appheader-logo-oracle{width:136px;height:19px}.demo-appheader-logo-oracle:before{content:url("images/oracle_logo_lrg.png")}@media (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 144dpi), (min-resolution: 1.5dppx){.demo-appheader-logo-oracle:before{content:url("images/oracle_logo_lrg_2x.png");-webkit-transform:translate(-25%, -25%) scale(0.5);transform:translate(-25%, -25%) scale(0.5)}}.demo-appheader-avatar{width:24px;height:24px}.demo-appheader-avatar:before{content:url("images/avatar_24px.png")}@media (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 144dpi), (min-resolution: 1.5dppx){.demo-appheader-avatar:before{content:url("images/avatar_24px_2x.png");-webkit-transform:translate(-25%, -25%) scale(0.5);transform:translate(-25%, -25%) scale(0.5)}}.demo-appheader-nav .oj-navigationlist-item-icon{color:#333}.demo-appheader-nav .oj-navigationlist-item-label{color:#4d4d4d}.oj-panel.demo-appheader-offcanvascontent{padding:0}.demo-appheader-offcanvascontent{color:#c0c4c8;background-color:#313334}.demo-appheader-toggle-button{margin-bottom:4px}.demo-appheader-offcanvascontent .oj-navigationlist-item.oj-selected{background-color:#202324}.demo-appheader-offcanvascontent .oj-navigationlist-item.oj-hover:not(.oj-selected){background-color:#262628;color:#fafafa}.demo-appheader-offcanvascontent a.oj-navigationlist-item-content{color:#c0c4c8}.demo-appheader-offcanvascontent .oj-navigationlist-item.oj-hover .oj-navigationlist-item-icon{color:#85bbe7}.demo-appheader-offcanvascontent .oj-navigationlist-item.oj-hover .oj-navigationlist-item-label{color:#85bbe7}.demo-appheader-offcanvascontent .oj-navigationlist-item.oj-selected .oj-navigationlist-item-label,.demo-appheader-offcanvascontent .oj-navigationlist-item.oj-selected .oj-navigationlist-item-icon{color:#fafafa}.demo-appheader-nav .oj-navigationlist-item.oj-hover:not(.oj-selected){background-color:transparent;color:#85bbe7}.demo-appheader-nav .oj-navigationlist-item.oj-hover .oj-navigationlist-item-icon,.demo-appheader-nav .oj-navigationlist-item.oj-hover .oj-navigationlist-item-label{color:#85bbe7}.demo-appheader-nav .oj-navigationlist-item.oj-selected{background-color:transparent;color:#0572ce}.demo-appheader-nav .oj-navigationlist-item.oj-selected .oj-navigationlist-item-icon,.demo-appheader-nav .oj-navigationlist-item.oj-selected .oj-navigationlist-item-label{color:#0572ce}.demo-appcontent{margin-right:10px;margin-left:10px;margin-top:0.8571rem}.demo-appfooter{padding-left:10px;padding-right:10px;height:4.167em;border-top:1px solid #d9dfe3;background-color:#f0f0f0;box-sizing:border-box}.demo-appfooter-text{font-size:0.857rem;color:#4d4d4d}.demo-appfooter ul{margin-bottom:2px}html:not([dir="rtl"]) .demo-appfooter ul{padding-left:0px}html[dir="rtl"] .demo-appfooter ul{padding-right:0px}.demo-appfooter li{list-style:none;display:inline-block}html:not([dir="rtl"]) .demo-appfooter li{margin-right:5px;margin-left:0px;padding-right:5px;border-right:1px solid #d9dfe3}html[dir="rtl"] .demo-appfooter li{margin-left:5px;margin-right:0px;padding-left:5px;border-left:1px solid #d9dfe3}html:not([dir="rtl"]) .demo-appfooter li:last-child{border-right:none}html[dir="rtl"] .demo-appfooter li:last-child{border-left:none}.demo-flex-top-container{position:absolute;top:0;left:0;bottom:0;right:0}.demo-flex-container{display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-flex:1 0 auto;flex:1 0 auto;position:relative;min-height:100%}.demo-flex-header{-webkit-flex:0 0 auto;flex:0 0 auto}.demo-flex-footer{-webkit-flex:0 0 auto;flex:0 0 auto}.demo-flex-content{-webkit-flex:1 0 auto;flex:1 0 auto}.demo-flex-center{display:-webkit-flex;display:flex;-webkit-flex-direction:column;flex-direction:column;-webkit-flex:1 0 auto;flex:1 0 auto;box-sizing:border-box;position:relative;min-height:100vh}@media screen and (-webkit-min-device-pixel-ratio: 0){.demo-flex-center{min-height:100%}}html.demo-sticky-footer-layout,.demo-sticky-footer-layout body{height:100%}.demo-sticky-footer-layout{height:100%}.demo-sticky-footer-main-outer{min-height:100%}.demo-sticky-footer-main-inner{overflow:visible;padding-bottom:4.167em}.demo-sticky-footer-layout .demo-appfooter{position:relative;margin-top:-4.167em}@media print and (orientation: portrait), screen and (max-width: 1023px){.demo-wrap{display:-webkit-flex;display:flex;-webkit-flex-direction:column;flex-direction:column}.demo-nav{padding-top:20px;-webkit-order:2;order:2}}.demo-header-toolbar-container{-webkit-align-items:center;align-items:center}html:not([dir="rtl"]) .demo-cook-search-button{float:right;margin-left:3px}html[dir="rtl"] .demo-cook-search-button{float:left;margin-right:3px}.demo-cook-search-input{overflow:hidden;display:block;width:auto}.demo-cook-search-input .oj-form-control{min-width:initial}#recipeTitle{margin-top:22px;margin-bottom:22px}#recipeTitle h1{display:inline-block}#componentContent{overflow:auto}#collapsibleRecipe{margin-top:25px}#componentRecipeContent>ul:first-child,#componentRecipeContent>ol:first-child{margin-top:0}#cookbookTitle h1{margin-top:4px;min-height:23px}#componentShell.oj-panel{padding:0}.demo-recipe-spacing{margin:20px 0;width:100%}.demo-recipe-table th,.demo-recipe-table td{border:1px solid lightgrey;border-collapse:collapse;width:10em;text-align:center;padding:10px}.demo-info-table th,.demo-info-table td{border:1px solid lightgrey;border-collapse:collapse;padding:10px}.demo-search-icon{font-size:1rem}.demo-help-icon{padding-left:5px}#doNotCopy{color:red;font-weight:bold;font-size:large;margin-left:100px}@font-face{font-family:'platforms';src:url("../images/formfactor/platform.woff") format("woff");font-weight:normal;font-style:normal}[class^="demo-platform-icon-"],[class*=" demo-platform-icon-"]{font-family:'platforms';speak:none;font-style:normal;font-weight:normal;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;width:24px;height:24px;font-size:24px}.demo-platform-icon-blocked:before{content:"\e905"}.demo-platform-icon-projector:before{content:"\e900"}.demo-platform-icon-wrench:before{content:"\e901"}.demo-platform-icon-leaf:before{content:"\e902"}.demo-platform-icon-cloud:before{content:"\e903"}.demo-platform-icon-hipster2:before{content:"\e904"}.demo-platform-icon-web:before{content:"\e9c9"}.demo-platform-icon-ios:before{content:"\eabf"}.demo-platform-icon-android:before{content:"\eac1"}.demo-platform-icon-windows:before{content:"\eac3"}.demo-platform-icon-desktop:before{content:"\e956"}.demo-platform-icon-phone-portrait:before,.demo-platform-icon-phone-landscape:before{content:"\e958"}.demo-platform-icon-phone-landscape:before{-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}.demo-platform-icon-tablet-portrait:before,.demo-platform-icon-tablet-landscape:before{content:"\e959"}.demo-platform-icon-tablet-landscape:before{-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}#togglePinnedNavListButtonSet .oj-web-applayout-offcanvas-icon,#toggleNavListButton .oj-web-applayout-offcanvas-icon{height:24px;width:24px}#header .oj-default .demo-applayout-offcanvas-icon::before,#header .oj-focus-only .demo-applayout-offcanvas-icon::before{content:url("../../../../images/24px_offcanvas_icon_ena.svg")}#header html[dir="rtl"] .oj-default .demo-applayout-offcanvas-icon::before,#header html[dir="rtl"] .oj-focus-only .demo-applayout-offcanvas-icon::before{content:url("../../../../images/24px_offcanvas_icon_ena.svg")}#header .oj-hover .demo-applayout-offcanvas-icon::before,#header .oj-hover.oj-selected .demo-applayout-offcanvas-icon::before{content:url("../../../../images/24px_offcanvas_icon_ovr.svg")}#header html[dir="rtl"] .oj-hover .demo-applayout-offcanvas-icon::before,#header html[dir="rtl"] .oj-hover.oj-selected .demo-applayout-offcanvas-icon::before{content:url("../../../../images/24px_offcanvas_icon_ovr.svg")}#header .oj-active .demo-applayout-offcanvas-icon::before,#header .oj-selected .demo-applayout-offcanvas-icon::before{content:url("../../../../images/24px_offcanvas_icon_sel.svg")}#header html[dir="rtl"] .oj-active .demo-applayout-offcanvas-icon::before,#header html[dir="rtl"] .oj-selected .demo-applayout-offcanvas-icon::before{content:url("../../../../images/24px_offcanvas_icon_sel.svg")}#header .oj-disabled .demo-applayout-offcanvas-icon::before{content:url("../../../../images/24px_offcanvas_icon_ena.svg")}#header html[dir="rtl"] .oj-disabled .demo-applayout-offcanvas-icon::before{content:url("../../../../images/24px_offcanvas_icon_ena.svg")}.demo-applayout-offcanvas-icon{height:24px;width:24px}.demo-applayout-offcanvas-icon::before{display:inline-block}.demo-applayout-offcanvas-icon{font-family:"Alta Icon Font";line-height:1;display:inline-block;speak:none;font-style:normal;font-variant:normal;text-transform:none;text-align:center;box-sizing:content-box;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#togglePinnedNavListButtonSet .oj-button.oj-hover.oj-button-half-chrome{background-color:transparent;border-color:transparent}#togglePinnedNavListButtonSet .oj-button.oj-selected.oj-button-half-chrome{background-color:transparent;border-color:transparent}#toggleNavListButton.oj-button.oj-hover.oj-button-half-chrome{background-color:transparent;border-color:transparent}#toggleNavListButton.oj-button.oj-selected.oj-button-half-chrome{background-color:transparent;border-color:transparent}.demo-pinned-navlist .oj-web-applayout-content{display:-webkit-flex;display:flex;-webkit-flex-direction:column;flex-direction:column}.demo-pinned-navlist .demo-page-wrapper{-webkit-flex:auto;flex:auto;display:-webkit-flex;display:flex}.demo-pinned-navlist #navDrawer{position:relative}#page-container{-webkit-overflow-scrolling:touch}.markup-example:after,.script-example:after,.css-example:after{position:absolute;top:-1px;left:-1px;padding:3px 7px;font-size:12px;font-weight:bold;background-color:#f5f5f5;border:1px solid #ddd;color:#9da0a4;border-radius:4px 0 4px 0}.css-example:after{content:"CSS Editor"}.markup-example:after{content:"HTML Editor"}.script-example:after{content:"JS Editor"}.css-example,.markup-example,.script-example{clear:left;position:relative;padding:0 0 14px;background-color:#fff;border:1px solid #ddd;border-radius:4px}.script-example{padding-top:39px}#html-example,#script-example,#css-example{padding-top:1rem}.italic{font-style:italic}.bold{font-weight:bold}.underline{text-decoration:underline}.edit-button{float:right;position:absolute;right:20px;top:10px}.codeEditorInfo{top:10px;left:75px;float:right;position:absolute;font-size:13px;color:#4f4f4f;padding-top:5px}.demo-selectornames{font-size:12px;color:#9c5800;padding:2px 4px;white-space:nowrap;background-color:#f7f7f9;border:1px solid #e1e1e8;font-family:Consolas,Menlo,Monaco,"Source Code Pro","Courier New",Courier,monospace}#global-home-container{height:760px}.homeBox{position:absolute;box-sizing:border-box;display:inline-block;border:1px solid #d6dfe6;border-radius:3px;background-color:white;background-repeat:no-repeat;background-position:center top;cursor:pointer}.homeBox LABEL{position:absolute;width:100%;padding-left:10px;margin:0px;box-sizing:border-box;bottom:10px;color:#252525;font-weight:bold;font-size:16px}.homeBox .withCaret:after{content:" ";display:inline-block;vertical-align:middle;width:24px;height:24px;background-repeat:no-repeat;background-image:url("../../../images/caret.png")}#gettingStarted LABEL{font-size:24px;padding-left:0px;left:0;top:40px;bottom:auto;text-align:center}#gettingStarted.homeBox{left:20px;right:50%;top:20px;height:350px;margin-right:10px;background-position:center bottom;background-image:url("../../../images/iStock_000019915867_Medium.jpg")}#formControls.homeBox{left:50%;right:25%;top:20px;height:165px;margin-left:10px;margin-right:15px;background-image:url("../../../images/homeTextAndSelection.png")}#buttons.homeBox{left:75%;right:20px;top:20px;height:165px;margin-left:5px;background-image:url("../../../images/homeButtons.png")}#dataCollections.homeBox{left:50%;right:25%;top:205px;height:165px;margin-left:10px;margin-right:15px;background-image:url("../../../images/homeDataCollections.png")}#dataVisualizations.homeBox{left:75%;right:20px;top:205px;height:165px;margin-left:5px;background-image:url("../../../images/homeDataVisualizations.png")}#patterns.homeBox{left:20px;right:50%;top:390px;height:350px;margin-right:10px;background-image:url("../../../images/homePatterns.png")}#layout.homeBox{left:50%;right:20px;top:390px;height:165px;margin-left:10px;background-image:url("../../../images/homeLayoutAndNavigation.png")}#behaviorsAndAnimation.homeBox{left:50%;right:25%;top:575px;height:165px;margin-left:10px;margin-right:15px;background-image:url("../../../images/homeBehaviorsAndAnimations.png")}#media.homeBox{left:75%;right:20px;top:575px;height:165px;margin-left:5px;background-image:url("../../../images/homeMedia.png")}@media only screen and (-webkit-min-device-pixel-ratio: 2){.homeBox .withCaret:after{background-image:url("../../../images/caret@2x.png");-webkit-background-size:24px 24px}#gettingStarted.homeBox{background-image:url("../../../images/iStock_000019915867_Medium@2x.jpg");-webkit-background-size:421px 443px}#formControls.homeBox{background-image:url("../../../images/homeTextAndSelection@2x.png");-webkit-background-size:219px 116px}#buttons.homeBox{background-image:url("../../../images/homeButtons@2x.png");-webkit-background-size:219px 116px}#dataCollections.homeBox{background-image:url("../../../images/homeDataCollections@2x.png");-webkit-background-size:219px 116px}#dataVisualizations.homeBox{background-image:url("../../../images/homeDataVisualizations@2x.png");-webkit-background-size:219px 116px}#patterns.homeBox{background-image:url("../../../images/homePatterns@2x.png");-webkit-background-size:458px 305px}#layout.homeBox{background-image:url("../../../images/homeLayoutAndNavigation@2x.png");-webkit-background-size:458px 116px}#behaviorsAndAnimation.homeBox{background-image:url("../../../images/homeBehaviorsAndAnimations@2x.png");-webkit-background-size:219px 116px}#media.homeBox{background-image:url("../../../images/homeMedia@2x.png");-webkit-background-size:219px 116px}}@media only screen and (min-device-pixel-ratio: 2){.homeBox .withCaret:after{background-image:url("../../../images/caret@2x.png");background-size:24px 24px}#formControls.homeBox{background-image:url("../../../images/iStock_000019915867_Medium@2x.jpg");background-size:421px 443px}#textAndSelection.homeBox{background-image:url("../../../images/homeTextAndSelection@2x.png");background-size:219px 116px}#buttons.homeBox{background-image:url("../../../images/homeButtons@2x.png");background-size:219px 116px}#dataCollections.homeBox{background-image:url("../../../images/homeDataCollections@2x.png");background-size:219px 116px}#dataVisualizations.homeBox{background-image:url("../../../images/homeDataVisualizations@2x.png");background-size:219px 116px}#patterns.homeBox{background-image:url("../../../images/homePatterns@2x.png");background-size:458px 305px}#layout.homeBox{background-image:url("../../../images/homeLayoutAndNavigation@2x.png");background-size:458px 116px}#behaviorsAndAnimation.homeBox{background-image:url("../../../images/homeBehaviorsAndAnimations@2x.png");background-size:219px 116px}#media.homeBox{background-image:url("../../../images/homeMedia@2x.png");background-size:219px 116px}}@media only screen and (min-resolution: 2dppx){.homeBox .withCaret:after{background-image:url("../../../images/caret@2x.png");background-size:24px 24px}#gettingStarted.homeBox{background-image:url("../../../images/iStock_000019915867_Medium@2x.jpg");background-size:421px 443px}#formControls.homeBox{background-image:url("../../../images/homeTextAndSelection@2x.png");background-size:219px 116px}#buttons.homeBox{background-image:url("../../../images/homeButtons@2x.png");background-size:219px 116px}#dataCollections.homeBox{background-image:url("../../../images/homeDataCollections@2x.png");background-size:219px 116px}#dataVisualizations.homeBox{background-image:url("../../../images/homeDataVisualizations@2x.png");background-size:219px 116px}#patterns.homeBox{background-image:url("../../../images/homePatterns@2x.png");background-size:458px 305px}#layout.homeBox{background-image:url("../../../images/homeLayoutAndNavigation@2x.png");background-size:458px 116px}#behaviorsAndAnimation.homeBox{background-image:url("../../../images/homeBehaviorsAndAnimations@2x.png");background-size:219px 116px}#media.homeBox{background-image:url("../../../images/homeMedia@2x.png");background-size:219px 116px}}#documentsContainer{margin-top:25px}#documentsTabsContainer{margin-top:25px}#devGuideContentContainer{display:table;width:100%}#left_dev_guide_nav{overflow:hidden;width:250px;border-right:1px solid #d6dfe6;display:table-cell;padding-right:10px}#devGuideContent{display:table-cell;padding-left:10px}#examplesContentContainer,#oracleInternalContentContainer{position:relative}#examplesContent,#oracleInternalContent{margin-left:280px;min-height:300px}#left_examples_nav,#left_oracle_internal_nav{position:absolute;top:0;bottom:0;overflow:hidden;width:250px;margin-left:0;border-right:1px solid #d6dfe6;padding-right:10px}.oj-inline-icon{display:inline-block;vertical-align:top}#technologyStackInfoContainer{margin-top:30px;margin-bottom:30px}#technologyStackInfo{border:1px solid black}#technologyStackInfo tr td{border:1px solid black;padding:3px}.demo-sort-icon{height:16px;width:16px}.oj-default .demo-sort-icon:before,.oj-focus-only .demo-sort-icon:before{content:url("../../../../images/arrowupalphabetaz_16_ena.png")}.oj-hover .demo-sort-icon:before,.oj-hover.oj-selected .demo-sort-icon:before{content:url("../../../../images/arrowupalphabetaz_16_hov.png")}.oj-active .demo-sort-icon:before,.oj-selected .demo-sort-icon:before{content:url("../../../../images/arrowupalphabetaz_16_onb.png")}.oj-disabled .demo-sort-icon:before{content:url("../../../../images/arrowupalphabetaz_16_dis.png")}#componentListContainer{margin:-10px}#navItem{padding:10px 10px 0 0}.componentCategoryItemListview.oj-listview-card-layout{display:-webkit-flex;display:flex;-webkit-flex-wrap:wrap;flex-wrap:wrap;-webkit-justify-content:center;justify-content:center}.categorybanner{padding:36px 0 36px 6px;background-color:#0096E0;-webkit-transition:padding 0.4s ease-in-out;transition:padding 0.4s ease-in-out}.categorybanner.small{padding:3px 0px 3px 0px}.categorybanner.shrink{padding:3px 0px 3px 0px;position:fixed;z-index:1;left:0;right:0}.rootCollections_banner{background-color:#d97638}.rootControls_banner{background-color:#e55a37}.rootForms_banner{background-color:#ea5b6e}.rootFramework_banner{background-color:#d16ba6}.rootLayoutNav_banner{background-color:#2f9ad4}.rootPatterns_banner{background-color:#269e9e}.rootVisualizations_banner{background-color:#a5a2a2}.all_banner{background-color:#8d909b}.categorybanner h1{font-size:46px;color:#FFFFFF;padding:0;margin:0;line-height:40px;padding-left:20px;opacity:0.8;font-weight:lighter;font-family:"SF UI Display","Helvetica Neue","Roboto","Segoe UI",Arial,sans-serif;-webkit-transition:height 0.4s ease-in-out, width 0.4s ease-in-out,font-size 0.4s ease-in-out;transition:height 0.4s ease-in-out,width 0.4s ease-in-out,font-size 0.4s ease-in-out}.categorygrouplabel{font-size:26px;color:#FFFFFF;padding:0;margin:5px 0;line-height:40px;padding-left:20px;opacity:0.8;font-weight:lighter;font-family:"SF UI Display","Helvetica Neue","Roboto","Segoe UI",Arial,sans-serif}.categorybanner.small h1{font-size:26px}.categorybanner.shrink h1{font-size:26px;-webkit-transition:height 0.4s ease-in-out,width 0.4s ease-in-out,font-size 0.4s ease-in-out;transition:height 0.4s ease-in-out,width 0.4s ease-in-out,font-size 0.4s ease-in-out}.categorylist{list-style:none;width:100%;margin:20px}.categorylist.fixed{position:fixed;z-index:1;background-image:url("../../../libs/oj/v10.0.0/alta/images/texture.png");background-repeat:repeat;margin:0;padding:20px}#filterMenuButtonBar.fixed{position:fixed;z-index:1;background-image:url("../../../libs/oj/v10.0.0/alta/images/texture.png");background-repeat:repeat}.categoryitem{line-height:28px;cursor:default;padding-right:5px}.categoryitem a{width:100%;height:100%;padding:5px 15px;text-decoration:none;border-radius:15px;border:1px solid transparent}.categoryitem.categorySelected a,a.categorylink:hover{color:#ffffff}#rootCollections_category.categorySelected a,#rootCollections_category a:hover{background-color:#914f26}#rootControls_category.categorySelected a,#rootControls_category a:hover{background-color:#a64123}#rootForms_category.categorySelected a,#rootForms_category a:hover{background-color:#a6414c}#rootFramework_category.categorySelected a,#rootFramework_category a:hover{background-color:#8c4380}#rootLayoutNav_category.categorySelected a,#rootLayoutNav_category a:hover{background-color:#216a8a}#rootPatterns_category.categorySelected a,#rootPatterns_category a:hover{background-color:#186e6e}#rootVisualizations_category.categorySelected a,#rootVisualizations_category a:hover{background-color:#A5A2A2}#all_category.categorySelected a,#all_category a:hover{background-color:#5f6169}#componentListContainer .oj-listview{border-style:none;padding-right:5px}.componentCategoryItemListview .oj-listview-item{position:relative;background-image:none;margin-bottom:15px;padding:0}.componentCategoryItemListview .oj-listview-item img{display:block}.componentCategoryItemListview .oj-listview-item h4{font-size:16px;font-weight:normal}.componentCategoryItemListview .oj-listview-item.oj-hover,.componentCategoryItemListview .oj-listview-item.oj-hover+li.oj-listview-item{border-top-color:transparent}.componentInfo{margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:10px;background-color:#fff;text-decoration:none}.componentShortDesc{color:#333;white-space:normal}.rootCollections_border{border-color:#d97638}.rootControls_border{border-color:#e55a37}.rootForms_border{border-color:#ea5b6e}.rootFramework_border{border-color:#d16ba6}.rootLayoutNav_border{border-color:#2f9ad4}.rootPatterns_border{border-color:#269e9e}.rootVisualizations_border{border-color:#a5a2a2}.all_border{border-color:#8d909b}.listingRef,.listingRef:hover,.listingRef:focus{text-decoration:none;cursor:default}.ribbon{position:absolute;right:-5px;top:-5px;z-index:1;overflow:hidden;width:75px;height:75px;text-align:right}.ribbon span{font-size:10px;color:#fff;text-transform:uppercase;text-align:center;font-weight:bold;line-height:20px;transform:rotate(45deg);width:100px;display:block;background-color:gray;box-shadow:0 3px 10px -5px #000;position:absolute;top:19px;right:-21px}.ribbon span::before{content:'';position:absolute;left:0px;top:100%;z-index:-1;border-left:3px solid gray;border-right:3px solid transparent;border-bottom:3px solid transparent;border-top:3px solid gray}.ribbon span::after{content:'';position:absolute;right:0%;top:100%;z-index:-1;border-right:3px solid gray;border-left:3px solid transparent;border-bottom:3px solid transparent;border-top:3px solid gray}[data-bind*="ojComponent"]:not(.oj-component-initnode){visibility:hidden}.global-container{position:relative}.boxSizing{box-sizing:border-box;position:relative}.pattern-nav{float:right}.demo-panel-contrast1{background-color:#313334;color:#fff}.demo-padding{background-image:none;margin:5px} +@font-face{font-family:'AppFont';src:url("fonts/App_iconfont.woff") format("woff");font-weight:normal;font-style:normal}.demo-icon-font,.demo-icon-font-24{font-family:AppFont;font-size:16px;line-height:1;display:inline-block;font-weight:normal;speak:none;font-style:normal;font-variant:normal;text-transform:none;text-align:center;box-sizing:content-box;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.demo-icon-font:before,.demo-icon-font-24:before{display:inline-block}.demo-icon-font-24{font-size:24px}.demo-bookmark-icon-16:before{content:"\e606"}.demo-grid-icon-16:before{content:"\e607"}.demo-list-icon-16:before{content:"\e608"}.demo-gear-icon-16:before{content:"\e609"}.demo-filter-icon-16:before{content:"\e907"}.demo-share-icon-16:before{content:"\e90a"}.demo-home-icon-24:before{content:"\e600"}.demo-education-icon-24:before{content:"\e601"}.demo-catalog-icon-24:before{content:"\e602"}.demo-palette-icon-24:before{content:"\e603"}.demo-library-icon-24:before{content:"\e604"}.demo-chat-icon-24:before{content:"\e605"}.demo-edit-icon-24:before{content:"\e906"}.demo-fire-icon-24:before{content:"\e908"}.demo-person-icon-24:before{content:"\e909"}.demo-signout-icon-24:before{content:"\e90b"}.demo-info-icon-24:before{content:"\e900"}.demo-copy-icon-24:before{content:"\e901"}.demo-people-icon-24:before{content:"\e902"}.demo-chart-icon-24:before{content:"\e903"}.demo-garbage-icon-24:before{content:"\e904"}.demo-download-icon-24:before{content:"\e905"}.demo-oracle-icon{width:137px;height:18px}.demo-oracle-icon:before{content:url("images/oracle_logo.svg")}html:not([dir="rtl"]) .demo-oracle-icon{padding-right:4px}html[dir="rtl"] .demo-oracle-icon{padding-left:4px}.demo-oracle-icon:before{display:inline}.demo-appheader{background-color:#f0f0f0;box-shadow:0 3px 3px rgba(0,0,0,0.1);position:relative;margin-bottom:1px}.oj-slow-boxshadow .demo-appheader{border-bottom-color:#d9dfe3}.demo-appheader-appname{font-size:1.143rem;font-weight:normal;color:#4d4d4d}html:not([dir="rtl"]) .demo-appheader-toolbar-container{text-align:right}html[dir="rtl"] .demo-appheader-toolbar-container{text-align:left}.demo-appheader-toolbar-container .oj-toolbar{display:inline-block;margin-right:0;margin-left:0}.demo-appheader-logo-block{display:inline-block}.demo-appheader-offcanvas-pushwrapper{width:100%;overflow-x:hidden}.demo-appheader-appname-block{vertical-align:2px}html:not([dir="rtl"]) .demo-appheader-appname-block{margin-left:4px}html[dir="rtl"] .demo-appheader-appname-block{margin-right:4px}.demo-offcanvas-toggle{display:block}html:not([dir="rtl"]) .demo-offcanvas-toggle{float:left}html[dir="rtl"] .demo-offcanvas-toggle{float:right}.demo-appheader-classic .demo-appheader-nav{display:none}@media screen and (max-width: 767px){.demo-appheader{padding-left:2px;padding-right:2px}.demo-appheader-appname-block,.demo-appheader-pulldown-normal,.demo-appheader-toolbar-separator,.demo-appheader-toolbar-button{display:none}.demo-appheader-pulldown-small{display:inline-block}.demo-appheader-classic .demo-offcanvas-toggle{width:30%;position:relative}.demo-appheader-classic .demo-appheader-logo{text-align:center;float:none;width:40%;position:relative}html:not([dir="rtl"]) .demo-appheader-classic .demo-appheader-logo{margin-left:0}html[dir="rtl"] .demo-appheader-classic .demo-appheader-logo{margin-right:0}}@media print, screen and (min-width: 768px){html:not([dir="rtl"]) .demo-offcanvas-toggle{margin-left:-10px}html[dir="rtl"] .demo-offcanvas-toggle{margin-right:-10px}.demo-appheader-appname-block,.demo-appheader-pulldown-normal{display:inline-block}.demo-appheader-pulldown-small{display:none}.demo-appheader{padding-left:10px;padding-right:10px}}@media print and (orientation: landscape), screen and (min-width: 1024px){.demo-offcanvas-toggle{display:none}.demo-appheader-appname-block{display:inline-block}html:not([dir="rtl"]) .demo-appheader-classic .demo-appheader-logo{margin-left:0}html[dir="rtl"] .demo-appheader-classic .demo-appheader-logo{margin-right:0}.demo-appheader-classic .demo-appheader-nav{display:block}html:not([dir="rtl"]) .demo-appheader-classic .demo-appheader-nav{text-align:right}html[dir="rtl"] .demo-appheader-classic .demo-appheader-nav{text-align:left}}.demo-appheader-lite .demo-appheader-logo{margin-top:9px}html:not([dir="rtl"]) .demo-appheader-lite .demo-appheader-logo{float:left}html[dir="rtl"] .demo-appheader-lite .demo-appheader-logo{float:right}.demo-appheader-lite .oj-toolbar{margin-top:0}.demo-appheader-classic .demo-appheader-logo{margin-top:8px}html:not([dir="rtl"]) .demo-appheader-classic .demo-appheader-logo{float:left;margin-left:4px}html[dir="rtl"] .demo-appheader-classic .demo-appheader-logo{float:right;margin-right:4px}.demo-appheader-logo-oracle{padding-top:3px}.demo-appheader-logo-oracle{width:136px;height:19px}.demo-appheader-logo-oracle:before{content:url("images/oracle_logo_lrg.png")}@media (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 144dpi), (min-resolution: 1.5dppx){.demo-appheader-logo-oracle:before{content:url("images/oracle_logo_lrg_2x.png");-webkit-transform:translate(-25%, -25%) scale(0.5);transform:translate(-25%, -25%) scale(0.5)}}.demo-appheader-avatar{width:24px;height:24px}.demo-appheader-avatar:before{content:url("images/avatar_24px.png")}@media (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 144dpi), (min-resolution: 1.5dppx){.demo-appheader-avatar:before{content:url("images/avatar_24px_2x.png");-webkit-transform:translate(-25%, -25%) scale(0.5);transform:translate(-25%, -25%) scale(0.5)}}.demo-appheader-nav .oj-navigationlist-item-icon{color:#333}.demo-appheader-nav .oj-navigationlist-item-label{color:#4d4d4d}.oj-panel.demo-appheader-offcanvascontent{padding:0}.demo-appheader-offcanvascontent{color:#c0c4c8;background-color:#313334}.demo-appheader-toggle-button{margin-bottom:4px}.demo-appheader-offcanvascontent .oj-navigationlist-item.oj-selected{background-color:#202324}.demo-appheader-offcanvascontent .oj-navigationlist-item.oj-hover:not(.oj-selected){background-color:#262628;color:#fafafa}.demo-appheader-offcanvascontent a.oj-navigationlist-item-content{color:#c0c4c8}.demo-appheader-offcanvascontent .oj-navigationlist-item.oj-hover .oj-navigationlist-item-icon{color:#85bbe7}.demo-appheader-offcanvascontent .oj-navigationlist-item.oj-hover .oj-navigationlist-item-label{color:#85bbe7}.demo-appheader-offcanvascontent .oj-navigationlist-item.oj-selected .oj-navigationlist-item-label,.demo-appheader-offcanvascontent .oj-navigationlist-item.oj-selected .oj-navigationlist-item-icon{color:#fafafa}.demo-appheader-nav .oj-navigationlist-item.oj-hover:not(.oj-selected){background-color:transparent;color:#85bbe7}.demo-appheader-nav .oj-navigationlist-item.oj-hover .oj-navigationlist-item-icon,.demo-appheader-nav .oj-navigationlist-item.oj-hover .oj-navigationlist-item-label{color:#85bbe7}.demo-appheader-nav .oj-navigationlist-item.oj-selected{background-color:transparent;color:#0572ce}.demo-appheader-nav .oj-navigationlist-item.oj-selected .oj-navigationlist-item-icon,.demo-appheader-nav .oj-navigationlist-item.oj-selected .oj-navigationlist-item-label{color:#0572ce}.demo-appcontent{margin-right:10px;margin-left:10px;margin-top:0.8571rem}.demo-appfooter{padding-left:10px;padding-right:10px;height:4.167em;border-top:1px solid #d9dfe3;background-color:#f0f0f0;box-sizing:border-box}.demo-appfooter-text{font-size:0.857rem;color:#4d4d4d}.demo-appfooter ul{margin-bottom:2px}html:not([dir="rtl"]) .demo-appfooter ul{padding-left:0px}html[dir="rtl"] .demo-appfooter ul{padding-right:0px}.demo-appfooter li{list-style:none;display:inline-block}html:not([dir="rtl"]) .demo-appfooter li{margin-right:5px;margin-left:0px;padding-right:5px;border-right:1px solid #d9dfe3}html[dir="rtl"] .demo-appfooter li{margin-left:5px;margin-right:0px;padding-left:5px;border-left:1px solid #d9dfe3}html:not([dir="rtl"]) .demo-appfooter li:last-child{border-right:none}html[dir="rtl"] .demo-appfooter li:last-child{border-left:none}.demo-flex-top-container{position:absolute;top:0;left:0;bottom:0;right:0}.demo-flex-container{display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-flex:1 0 auto;flex:1 0 auto;position:relative;min-height:100%}.demo-flex-header{-webkit-flex:0 0 auto;flex:0 0 auto}.demo-flex-footer{-webkit-flex:0 0 auto;flex:0 0 auto}.demo-flex-content{-webkit-flex:1 0 auto;flex:1 0 auto}.demo-flex-center{display:-webkit-flex;display:flex;-webkit-flex-direction:column;flex-direction:column;-webkit-flex:1 0 auto;flex:1 0 auto;box-sizing:border-box;position:relative;min-height:100vh}@media screen and (-webkit-min-device-pixel-ratio: 0){.demo-flex-center{min-height:100%}}html.demo-sticky-footer-layout,.demo-sticky-footer-layout body{height:100%}.demo-sticky-footer-layout{height:100%}.demo-sticky-footer-main-outer{min-height:100%}.demo-sticky-footer-main-inner{overflow:visible;padding-bottom:4.167em}.demo-sticky-footer-layout .demo-appfooter{position:relative;margin-top:-4.167em}@media print and (orientation: portrait), screen and (max-width: 1023px){.demo-wrap{display:-webkit-flex;display:flex;-webkit-flex-direction:column;flex-direction:column}.demo-nav{padding-top:20px;-webkit-order:2;order:2}}.demo-header-toolbar-container{-webkit-align-items:center;align-items:center}html:not([dir="rtl"]) .demo-cook-search-button{float:right;margin-left:3px}html[dir="rtl"] .demo-cook-search-button{float:left;margin-right:3px}.demo-cook-search-input{overflow:hidden;display:block;width:auto}.demo-cook-search-input .oj-form-control{min-width:initial}#recipeTitle{margin-top:22px;margin-bottom:22px}#recipeTitle h1{display:inline-block}#componentContent{overflow:auto}#collapsibleRecipe{margin-top:25px}#componentRecipeContent>ul:first-child,#componentRecipeContent>ol:first-child{margin-top:0}#cookbookTitle h1{margin-top:4px;min-height:23px}#componentShell.oj-panel{padding:0}.demo-recipe-spacing{margin:20px 0;width:100%}.demo-recipe-table th,.demo-recipe-table td{border:1px solid lightgrey;border-collapse:collapse;width:10em;text-align:center;padding:10px}.demo-info-table th,.demo-info-table td{border:1px solid lightgrey;border-collapse:collapse;padding:10px}.demo-search-icon{font-size:1rem}.demo-help-icon{padding-left:5px}#doNotCopy{color:red;font-weight:bold;font-size:large;margin-left:100px}@font-face{font-family:'platforms';src:url("../images/formfactor/platform.woff") format("woff");font-weight:normal;font-style:normal}[class^="demo-platform-icon-"],[class*=" demo-platform-icon-"]{font-family:'platforms';speak:none;font-style:normal;font-weight:normal;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;width:24px;height:24px;font-size:24px}.demo-platform-icon-blocked:before{content:"\e905"}.demo-platform-icon-projector:before{content:"\e900"}.demo-platform-icon-wrench:before{content:"\e901"}.demo-platform-icon-leaf:before{content:"\e902"}.demo-platform-icon-cloud:before{content:"\e903"}.demo-platform-icon-hipster2:before{content:"\e904"}.demo-platform-icon-web:before{content:"\e9c9"}.demo-platform-icon-ios:before{content:"\eabf"}.demo-platform-icon-android:before{content:"\eac1"}.demo-platform-icon-windows:before{content:"\eac3"}.demo-platform-icon-desktop:before{content:"\e956"}.demo-platform-icon-phone-portrait:before,.demo-platform-icon-phone-landscape:before{content:"\e958"}.demo-platform-icon-phone-landscape:before{-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}.demo-platform-icon-tablet-portrait:before,.demo-platform-icon-tablet-landscape:before{content:"\e959"}.demo-platform-icon-tablet-landscape:before{-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}#togglePinnedNavListButtonSet .oj-web-applayout-offcanvas-icon,#toggleNavListButton .oj-web-applayout-offcanvas-icon{height:24px;width:24px}#header .oj-default .demo-applayout-offcanvas-icon::before,#header .oj-focus-only .demo-applayout-offcanvas-icon::before{content:url("../../../../images/24px_offcanvas_icon_ena.svg")}#header html[dir="rtl"] .oj-default .demo-applayout-offcanvas-icon::before,#header html[dir="rtl"] .oj-focus-only .demo-applayout-offcanvas-icon::before{content:url("../../../../images/24px_offcanvas_icon_ena.svg")}#header .oj-hover .demo-applayout-offcanvas-icon::before,#header .oj-hover.oj-selected .demo-applayout-offcanvas-icon::before{content:url("../../../../images/24px_offcanvas_icon_ovr.svg")}#header html[dir="rtl"] .oj-hover .demo-applayout-offcanvas-icon::before,#header html[dir="rtl"] .oj-hover.oj-selected .demo-applayout-offcanvas-icon::before{content:url("../../../../images/24px_offcanvas_icon_ovr.svg")}#header .oj-active .demo-applayout-offcanvas-icon::before,#header .oj-selected .demo-applayout-offcanvas-icon::before{content:url("../../../../images/24px_offcanvas_icon_sel.svg")}#header html[dir="rtl"] .oj-active .demo-applayout-offcanvas-icon::before,#header html[dir="rtl"] .oj-selected .demo-applayout-offcanvas-icon::before{content:url("../../../../images/24px_offcanvas_icon_sel.svg")}#header .oj-disabled .demo-applayout-offcanvas-icon::before{content:url("../../../../images/24px_offcanvas_icon_ena.svg")}#header html[dir="rtl"] .oj-disabled .demo-applayout-offcanvas-icon::before{content:url("../../../../images/24px_offcanvas_icon_ena.svg")}.demo-applayout-offcanvas-icon{height:24px;width:24px}.demo-applayout-offcanvas-icon::before{display:inline-block}.demo-applayout-offcanvas-icon{font-family:"Alta Icon Font";line-height:1;display:inline-block;speak:none;font-style:normal;font-variant:normal;text-transform:none;text-align:center;box-sizing:content-box;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#togglePinnedNavListButtonSet .oj-button.oj-hover.oj-button-half-chrome{background-color:transparent;border-color:transparent}#togglePinnedNavListButtonSet .oj-button.oj-selected.oj-button-half-chrome{background-color:transparent;border-color:transparent}#toggleNavListButton.oj-button.oj-hover.oj-button-half-chrome{background-color:transparent;border-color:transparent}#toggleNavListButton.oj-button.oj-selected.oj-button-half-chrome{background-color:transparent;border-color:transparent}.demo-pinned-navlist .oj-web-applayout-content{display:-webkit-flex;display:flex;-webkit-flex-direction:column;flex-direction:column}.demo-pinned-navlist .demo-page-wrapper{-webkit-flex:auto;flex:auto;display:-webkit-flex;display:flex}.demo-pinned-navlist #navDrawer{position:relative}#page-container{-webkit-overflow-scrolling:touch}.markup-example:after,.script-example:after,.css-example:after{position:absolute;top:-1px;left:-1px;padding:3px 7px;font-size:12px;font-weight:bold;background-color:#f5f5f5;border:1px solid #ddd;color:#9da0a4;border-radius:4px 0 4px 0}.css-example:after{content:"CSS Editor"}.markup-example:after{content:"HTML Editor"}.script-example:after{content:"JS Editor"}.css-example,.markup-example,.script-example{clear:left;position:relative;padding:0 0 14px;background-color:#fff;border:1px solid #ddd;border-radius:4px}.script-example{padding-top:39px}#html-example,#script-example,#css-example{padding-top:1rem}.italic{font-style:italic}.bold{font-weight:bold}.underline{text-decoration:underline}.edit-button{float:right;position:absolute;right:20px;top:10px}.codeEditorInfo{top:10px;left:75px;float:right;position:absolute;font-size:13px;color:#4f4f4f;padding-top:5px}.demo-selectornames{font-size:12px;color:#9c5800;padding:2px 4px;white-space:nowrap;background-color:#f7f7f9;border:1px solid #e1e1e8;font-family:Consolas,Menlo,Monaco,"Source Code Pro","Courier New",Courier,monospace}#global-home-container{height:760px}.homeBox{position:absolute;box-sizing:border-box;display:inline-block;border:1px solid #d6dfe6;border-radius:3px;background-color:white;background-repeat:no-repeat;background-position:center top;cursor:pointer}.homeBox LABEL{position:absolute;width:100%;padding-left:10px;margin:0px;box-sizing:border-box;bottom:10px;color:#252525;font-weight:bold;font-size:16px}.homeBox .withCaret:after{content:" ";display:inline-block;vertical-align:middle;width:24px;height:24px;background-repeat:no-repeat;background-image:url("../../../images/caret.png")}#gettingStarted LABEL{font-size:24px;padding-left:0px;left:0;top:40px;bottom:auto;text-align:center}#gettingStarted.homeBox{left:20px;right:50%;top:20px;height:350px;margin-right:10px;background-position:center bottom;background-image:url("../../../images/iStock_000019915867_Medium.jpg")}#formControls.homeBox{left:50%;right:25%;top:20px;height:165px;margin-left:10px;margin-right:15px;background-image:url("../../../images/homeTextAndSelection.png")}#buttons.homeBox{left:75%;right:20px;top:20px;height:165px;margin-left:5px;background-image:url("../../../images/homeButtons.png")}#dataCollections.homeBox{left:50%;right:25%;top:205px;height:165px;margin-left:10px;margin-right:15px;background-image:url("../../../images/homeDataCollections.png")}#dataVisualizations.homeBox{left:75%;right:20px;top:205px;height:165px;margin-left:5px;background-image:url("../../../images/homeDataVisualizations.png")}#patterns.homeBox{left:20px;right:50%;top:390px;height:350px;margin-right:10px;background-image:url("../../../images/homePatterns.png")}#layout.homeBox{left:50%;right:20px;top:390px;height:165px;margin-left:10px;background-image:url("../../../images/homeLayoutAndNavigation.png")}#behaviorsAndAnimation.homeBox{left:50%;right:25%;top:575px;height:165px;margin-left:10px;margin-right:15px;background-image:url("../../../images/homeBehaviorsAndAnimations.png")}#media.homeBox{left:75%;right:20px;top:575px;height:165px;margin-left:5px;background-image:url("../../../images/homeMedia.png")}@media only screen and (-webkit-min-device-pixel-ratio: 2){.homeBox .withCaret:after{background-image:url("../../../images/caret@2x.png");-webkit-background-size:24px 24px}#gettingStarted.homeBox{background-image:url("../../../images/iStock_000019915867_Medium@2x.jpg");-webkit-background-size:421px 443px}#formControls.homeBox{background-image:url("../../../images/homeTextAndSelection@2x.png");-webkit-background-size:219px 116px}#buttons.homeBox{background-image:url("../../../images/homeButtons@2x.png");-webkit-background-size:219px 116px}#dataCollections.homeBox{background-image:url("../../../images/homeDataCollections@2x.png");-webkit-background-size:219px 116px}#dataVisualizations.homeBox{background-image:url("../../../images/homeDataVisualizations@2x.png");-webkit-background-size:219px 116px}#patterns.homeBox{background-image:url("../../../images/homePatterns@2x.png");-webkit-background-size:458px 305px}#layout.homeBox{background-image:url("../../../images/homeLayoutAndNavigation@2x.png");-webkit-background-size:458px 116px}#behaviorsAndAnimation.homeBox{background-image:url("../../../images/homeBehaviorsAndAnimations@2x.png");-webkit-background-size:219px 116px}#media.homeBox{background-image:url("../../../images/homeMedia@2x.png");-webkit-background-size:219px 116px}}@media only screen and (min-device-pixel-ratio: 2){.homeBox .withCaret:after{background-image:url("../../../images/caret@2x.png");background-size:24px 24px}#formControls.homeBox{background-image:url("../../../images/iStock_000019915867_Medium@2x.jpg");background-size:421px 443px}#textAndSelection.homeBox{background-image:url("../../../images/homeTextAndSelection@2x.png");background-size:219px 116px}#buttons.homeBox{background-image:url("../../../images/homeButtons@2x.png");background-size:219px 116px}#dataCollections.homeBox{background-image:url("../../../images/homeDataCollections@2x.png");background-size:219px 116px}#dataVisualizations.homeBox{background-image:url("../../../images/homeDataVisualizations@2x.png");background-size:219px 116px}#patterns.homeBox{background-image:url("../../../images/homePatterns@2x.png");background-size:458px 305px}#layout.homeBox{background-image:url("../../../images/homeLayoutAndNavigation@2x.png");background-size:458px 116px}#behaviorsAndAnimation.homeBox{background-image:url("../../../images/homeBehaviorsAndAnimations@2x.png");background-size:219px 116px}#media.homeBox{background-image:url("../../../images/homeMedia@2x.png");background-size:219px 116px}}@media only screen and (min-resolution: 2dppx){.homeBox .withCaret:after{background-image:url("../../../images/caret@2x.png");background-size:24px 24px}#gettingStarted.homeBox{background-image:url("../../../images/iStock_000019915867_Medium@2x.jpg");background-size:421px 443px}#formControls.homeBox{background-image:url("../../../images/homeTextAndSelection@2x.png");background-size:219px 116px}#buttons.homeBox{background-image:url("../../../images/homeButtons@2x.png");background-size:219px 116px}#dataCollections.homeBox{background-image:url("../../../images/homeDataCollections@2x.png");background-size:219px 116px}#dataVisualizations.homeBox{background-image:url("../../../images/homeDataVisualizations@2x.png");background-size:219px 116px}#patterns.homeBox{background-image:url("../../../images/homePatterns@2x.png");background-size:458px 305px}#layout.homeBox{background-image:url("../../../images/homeLayoutAndNavigation@2x.png");background-size:458px 116px}#behaviorsAndAnimation.homeBox{background-image:url("../../../images/homeBehaviorsAndAnimations@2x.png");background-size:219px 116px}#media.homeBox{background-image:url("../../../images/homeMedia@2x.png");background-size:219px 116px}}#documentsContainer{margin-top:25px}#documentsTabsContainer{margin-top:25px}#devGuideContentContainer{display:table;width:100%}#left_dev_guide_nav{overflow:hidden;width:250px;border-right:1px solid #d6dfe6;display:table-cell;padding-right:10px}#devGuideContent{display:table-cell;padding-left:10px}#examplesContentContainer,#oracleInternalContentContainer{position:relative}#examplesContent,#oracleInternalContent{margin-left:280px;min-height:300px}#left_examples_nav,#left_oracle_internal_nav{position:absolute;top:0;bottom:0;overflow:hidden;width:250px;margin-left:0;border-right:1px solid #d6dfe6;padding-right:10px}.oj-inline-icon{display:inline-block;vertical-align:top}#technologyStackInfoContainer{margin-top:30px;margin-bottom:30px}#technologyStackInfo{border:1px solid black}#technologyStackInfo tr td{border:1px solid black;padding:3px}.demo-sort-icon{height:16px;width:16px}.oj-default .demo-sort-icon:before,.oj-focus-only .demo-sort-icon:before{content:url("../../../../images/arrowupalphabetaz_16_ena.png")}.oj-hover .demo-sort-icon:before,.oj-hover.oj-selected .demo-sort-icon:before{content:url("../../../../images/arrowupalphabetaz_16_hov.png")}.oj-active .demo-sort-icon:before,.oj-selected .demo-sort-icon:before{content:url("../../../../images/arrowupalphabetaz_16_onb.png")}.oj-disabled .demo-sort-icon:before{content:url("../../../../images/arrowupalphabetaz_16_dis.png")}#componentListContainer{margin:-10px}#navItem{padding:10px 10px 0 0}.componentCategoryItemListview.oj-listview-card-layout{display:-webkit-flex;display:flex;-webkit-flex-wrap:wrap;flex-wrap:wrap;-webkit-justify-content:center;justify-content:center}.categorybanner{padding:36px 0 36px 6px;background-color:#0096E0;-webkit-transition:padding 0.4s ease-in-out;transition:padding 0.4s ease-in-out}.categorybanner.small{padding:3px 0px 3px 0px}.categorybanner.shrink{padding:3px 0px 3px 0px;position:fixed;z-index:1;left:0;right:0}.rootCollections_banner{background-color:#d97638}.rootControls_banner{background-color:#e55a37}.rootForms_banner{background-color:#ea5b6e}.rootFramework_banner{background-color:#d16ba6}.rootLayoutNav_banner{background-color:#2f9ad4}.rootPatterns_banner{background-color:#269e9e}.rootVisualizations_banner{background-color:#a5a2a2}.all_banner{background-color:#8d909b}.categorybanner h1{font-size:46px;color:#FFFFFF;padding:0;margin:0;line-height:40px;padding-left:20px;opacity:0.8;font-weight:lighter;font-family:"SF UI Display","Helvetica Neue","Roboto","Segoe UI",Arial,sans-serif;-webkit-transition:height 0.4s ease-in-out, width 0.4s ease-in-out,font-size 0.4s ease-in-out;transition:height 0.4s ease-in-out,width 0.4s ease-in-out,font-size 0.4s ease-in-out}.categorygrouplabel{font-size:26px;color:#FFFFFF;padding:0;margin:5px 0;line-height:40px;padding-left:20px;opacity:0.8;font-weight:lighter;font-family:"SF UI Display","Helvetica Neue","Roboto","Segoe UI",Arial,sans-serif}.categorybanner.small h1{font-size:26px}.categorybanner.shrink h1{font-size:26px;-webkit-transition:height 0.4s ease-in-out,width 0.4s ease-in-out,font-size 0.4s ease-in-out;transition:height 0.4s ease-in-out,width 0.4s ease-in-out,font-size 0.4s ease-in-out}.categorylist{list-style:none;width:100%;margin:20px}.categorylist.fixed{position:fixed;z-index:1;background-image:url("../../../libs/oj/13.0.0/alta/images/texture.png");background-repeat:repeat;margin:0;padding:20px}#filterMenuButtonBar.fixed{position:fixed;z-index:1;background-image:url("../../../libs/oj/13.0.0/alta/images/texture.png");background-repeat:repeat}.categoryitem{line-height:28px;cursor:default;padding-right:5px}.categoryitem a{width:100%;height:100%;padding:5px 15px;text-decoration:none;border-radius:15px;border:1px solid transparent}.categoryitem.categorySelected a,a.categorylink:hover{color:#ffffff}#rootCollections_category.categorySelected a,#rootCollections_category a:hover{background-color:#914f26}#rootControls_category.categorySelected a,#rootControls_category a:hover{background-color:#a64123}#rootForms_category.categorySelected a,#rootForms_category a:hover{background-color:#a6414c}#rootFramework_category.categorySelected a,#rootFramework_category a:hover{background-color:#8c4380}#rootLayoutNav_category.categorySelected a,#rootLayoutNav_category a:hover{background-color:#216a8a}#rootPatterns_category.categorySelected a,#rootPatterns_category a:hover{background-color:#186e6e}#rootVisualizations_category.categorySelected a,#rootVisualizations_category a:hover{background-color:#A5A2A2}#all_category.categorySelected a,#all_category a:hover{background-color:#5f6169}#componentListContainer .oj-listview{border-style:none;padding-right:5px}.componentCategoryItemListview .oj-listview-item{position:relative;background-image:none;margin-bottom:15px;padding:0}.componentCategoryItemListview .oj-listview-item img{display:block}.componentCategoryItemListview .oj-listview-item h4{font-size:16px;font-weight:normal}.componentCategoryItemListview .oj-listview-item.oj-hover,.componentCategoryItemListview .oj-listview-item.oj-hover+li.oj-listview-item{border-top-color:transparent}.componentInfo{margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:10px;background-color:#fff;text-decoration:none}.componentShortDesc{color:#333;white-space:normal}.rootCollections_border{border-color:#d97638}.rootControls_border{border-color:#e55a37}.rootForms_border{border-color:#ea5b6e}.rootFramework_border{border-color:#d16ba6}.rootLayoutNav_border{border-color:#2f9ad4}.rootPatterns_border{border-color:#269e9e}.rootVisualizations_border{border-color:#a5a2a2}.all_border{border-color:#8d909b}.listingRef,.listingRef:hover,.listingRef:focus{text-decoration:none;cursor:default}.ribbon{position:absolute;right:-5px;top:-5px;z-index:1;overflow:hidden;width:75px;height:75px;text-align:right}.ribbon span{font-size:10px;color:#fff;text-transform:uppercase;text-align:center;font-weight:bold;line-height:20px;transform:rotate(45deg);width:100px;display:block;background-color:gray;box-shadow:0 3px 10px -5px #000;position:absolute;top:19px;right:-21px}.ribbon span::before{content:'';position:absolute;left:0px;top:100%;z-index:-1;border-left:3px solid gray;border-right:3px solid transparent;border-bottom:3px solid transparent;border-top:3px solid gray}.ribbon span::after{content:'';position:absolute;right:0%;top:100%;z-index:-1;border-right:3px solid gray;border-left:3px solid transparent;border-bottom:3px solid transparent;border-top:3px solid gray}[data-bind*="ojComponent"]:not(.oj-component-initnode){visibility:hidden}.global-container{position:relative}.boxSizing{box-sizing:border-box;position:relative}.pattern-nav{float:right}.demo-panel-contrast1{background-color:#313334;color:#fff}.demo-padding{background-image:none;margin:5px} diff --git a/test/templates/webTsApiTest/src/index.html b/test/templates/webTsApiTest/src/index.html index 6113c51..e378741 100644 --- a/test/templates/webTsApiTest/src/index.html +++ b/test/templates/webTsApiTest/src/index.html @@ -42,7 +42,7 @@ - + diff --git a/test/templates/webTsApiTest/src/js/main.js b/test/templates/webTsApiTest/src/js/main.js index 2ffbf7f..528a6a1 100644 --- a/test/templates/webTsApiTest/src/js/main.js +++ b/test/templates/webTsApiTest/src/js/main.js @@ -35,9 +35,9 @@ 'jqueryui-amd': 'libs/jquery/jqueryui-amd-1.13.0', 'hammerjs': 'libs/hammer/hammer-2.0.8', 'ojdnd': 'libs/dnd-polyfill/dnd-polyfill-1.0.2', - 'ojs': 'libs/oj/v10.0.0/debug', - 'ojL10n': 'libs/oj/v10.0.0/ojL10n', - 'ojtranslations': 'libs/oj/v10.0.0/resources', + 'ojs': 'libs/oj/13.0.0/debug', + 'ojL10n': 'libs/oj/13.0.0/ojL10n', + 'ojtranslations': 'libs/oj/13.0.0/resources', 'text': 'libs/require/text', 'signals': 'libs/js-signals/signals', 'customElements': 'libs/webcomponents/custom-elements.min', @@ -45,7 +45,7 @@ 'css': 'libs/require-css/css', 'touchr': 'libs/touchr/touchr', 'corejs' : 'libs/corejs/shim', - 'chai': 'libs/chai/chai-4.2.0', + 'chai': 'libs/chai/chai-4.3.6', 'regenerator-runtime' : 'libs/regenerator-runtime/runtime' } // endinjector diff --git a/test/templates/webTsApiTest/src/js/path_mapping.json b/test/templates/webTsApiTest/src/js/path_mapping.json index 25618d7..6412584 100644 --- a/test/templates/webTsApiTest/src/js/path_mapping.json +++ b/test/templates/webTsApiTest/src/js/path_mapping.json @@ -1,14 +1,17 @@ { + "baseUrl": "js", "use": "local", "cdns": { "jet": { - "prefix": "https://ci-cloud.us.oracle.com/jenkins/uitech/job/CDN_12.1.0/lastSuccessfulBuild/artifact/cdn/v12.1.0/default/js", - "css": "https://ci-cloud.us.oracle.com/jenkins/uitech/job/CDN_12.1.0/lastSuccessfulBuild/artifact/cdn/v12.1.0/default/css", + "prefix": "https://ci-cloud.us.oracle.com/jenkins/uitech/job/CDN_master/lastSuccessfulBuild/artifact/cdn/13.0.0/default/js", + "css": "https://ci-cloud.us.oracle.com/jenkins/uitech/job/CDN_master/lastSuccessfulBuild/artifact/cdn/13.0.0/default/css", + "csspreact": "https://ci-cloud.us.oracle.com/jenkins/uitech/job/CDN_master/lastSuccessfulBuild/artifact/cdn/13.0.0/3rdparty/oraclejet-preact/amd", "config": "bundles-config.js" }, - "3rdparty": "https://ci-cloud.us.oracle.com/jenkins/uitech/job/CDN_12.1.0/lastSuccessfulBuild/artifact/cdn/v12.1.0/3rdparty" + "3rdparty": "https://ci-cloud.us.oracle.com/jenkins/uitech/job/CDN_master/lastSuccessfulBuild/artifact/cdn/13.0.0/3rdparty" }, "libs": { + "knockout": { "cdn": "3rdparty", "cwd": "node_modules/knockout/build/output", @@ -23,6 +26,7 @@ "cdnPath": "knockout/knockout-3.5.1" } }, + "jquery": { "cdn": "3rdparty", "cwd": "node_modules/jquery/dist", @@ -37,32 +41,22 @@ "cdnPath": "jquery/jquery-3.6.0.min" } }, + "jqueryui-amd": { "cdn": "3rdparty", "cwd": "node_modules/jquery-ui/ui", "debug": { - "src": [ - "*.js", - "widgets/draggable.js", - "widgets/mouse.js", - "widgets/sortable.js", - "vendor/jquery-color/jquery.color.js" - ], + "src": ["*.js", "widgets/draggable.js", "widgets/mouse.js", "widgets/sortable.js", "vendor/jquery-color/jquery.color.js"], "path": "libs/jquery/jqueryui-amd-#{version}", "cdnPath": "jquery/jqueryui-amd-1.13.0" }, "release": { - "src": [ - "*.js", - "widgets/draggable.js", - "widgets/mouse.js", - "widgets/sortable.js", - "vendor/jquery-color/jquery.color.js" - ], + "src": ["*.js", "widgets/draggable.js", "widgets/mouse.js", "widgets/sortable.js", "vendor/jquery-color/jquery.color.js"], "path": "libs/jquery/jqueryui-amd-#{version}.min", "cdnPath": "jquery/jqueryui-amd-1.13.0.min" } }, + "hammerjs": { "cdn": "3rdparty", "cwd": "node_modules/hammerjs", @@ -77,6 +71,7 @@ "cdnPath": "hammer/hammer-2.0.8.min" } }, + "ojdnd": { "cdn": "3rdparty", "cwd": "node_modules/@oracle/oraclejet/dist/js/libs/dnd-polyfill", @@ -91,83 +86,106 @@ "cdnPath": "dnd-polyfill/dnd-polyfill-1.0.2.min" } }, + "ojs": { "cdn": "jet", "cwd": "node_modules/@oracle/oraclejet/dist/js/libs/oj", "debug": { - "src": [ - "debug*/**" - ], - "path": "libs/oj/v#{version}", + "src": ["debug*/**"], + "path": "libs/oj/#{version}", "pathSuffix": "/debug'", "cdnPath": "" }, "release": { - "src": [ - "min*/**" - ], - "path": "libs/oj/v#{version}", - "pathSuffix": "/min'", + "src": ["min*/**"], + "path": "libs/oj/#{version}", + "pathSuffix": "/min'", "cdnPath": "" } - }, + }, + "ojL10n": { "cdn": "jet", "cwd": "node_modules/@oracle/oraclejet/dist/js/libs/oj", "debug": { - "src": "ojL10n.js", - "path": "libs/oj/v#{version}/ojL10n.js", + "src": "ojL10n.js", + "path": "libs/oj/#{version}/ojL10n.js", "cdnPath": "ojL10n" }, "release": { "src": "ojL10n.js", - "path": "libs/oj/v#{version}/ojL10n.js", + "path": "libs/oj/#{version}/ojL10n.js", "cdnPath": "ojL10n" } }, + "ojtranslations": { "cdn": "jet", "cwd": "node_modules/@oracle/oraclejet/dist/js/libs/oj/resources", "debug": { - "src": [ - "**" - ], - "path": "libs/oj/v#{version}/resources", + "src": ["**"], + "path": "libs/oj/#{version}/resources", "cdnPath": "resources" }, "release": { - "src": [ - "**" - ], - "path": "libs/oj/v#{version}/resources", + "src": ["**"], + "path": "libs/oj/#{version}/resources", "cdnPath": "resources" } }, + + "@oracle/oraclejet-preact": { + "cdn": "3rdparty", + "cwd": "node_modules/@oracle/oraclejet/dist/js/libs/oraclejet-preact/amd", + "debug": { + "src": ["**"], + "path": "libs/oraclejet-preact/amd", + "cdnPath": "oraclejet-preact/amd" + }, + "release": { + "src": ["**"], + "path": "libs/oraclejet-preact/amd", + "cdnPath": "oraclejet-preact/amd" + } + }, + + "oj-c": { + "cdn": "3rdparty", + "cwd": "node_modules/@oracle/oraclejet-core-pack/oj-c", + "debug": { + "src": ["**"], + "path": "libs/packs/oj-c", + "cdnPath": "../../packs/oj-c/#{version}" + }, + "release": { + "src": ["**"], + "path": "libs/packs/oj-c/min", + "cdnPath": "../../packs/oj-c/#{version}/min" + } + }, + "persist": { "cdn": "3rdparty", "cwd": "node_modules/@oracle/oraclejet/dist/js/libs/persist", "debug": { "cwd": "debug", - "src": [ - "**" - ], + "src": ["**"], "path": "libs/persist/debug", "cdnPath": "persist/debug" }, "release": { "cwd": "min", - "src": [ - "**" - ], + "src": ["**"], "path": "libs/persist/min", "cdnPath": "persist/min" } }, + "text": { "cdn": "3rdparty", "cwd": "node_modules/requirejs-text", "debug": { - "src": "text.js", + "src": "text.js", "path": "libs/require/text.js", "cdnPath": "require/text" }, @@ -177,11 +195,12 @@ "cdnPath": "require/text" } }, + "signals": { "cdn": "3rdparty", "cwd": "node_modules/signals/dist", "debug": { - "src": "signals.js", + "src": "signals.js", "path": "libs/js-signals/signals.js", "cdnPath": "js-signals/signals" }, @@ -191,11 +210,12 @@ "cdnPath": "js-signals/signals.min" } }, + "touchr": { "cdn": "3rdparty", "cwd": "node_modules/@oracle/oraclejet/dist/js/libs/touchr", "debug": { - "src": "touchr.js", + "src": "touchr.js", "path": "libs/touchr/touchr.js", "cdnPath": "touchr/touchr" }, @@ -205,46 +225,37 @@ "cdnPath": "touchr/touchr" } }, + "preact": { "cdn": "3rdparty", "cwd": "node_modules/preact/dist", "debug": { - "src": [ - "preact.umd.js", - "preact.umd.js.map" - ], + "src": ["preact.umd.js", "preact.umd.js.map"], "path": "libs/preact/dist/preact.umd.js", "cdnPath": "preact/dist/preact.umd" }, "release": { - "src": [ - "preact.umd.js", - "preact.umd.js.map" - ], + "src": ["preact.umd.js", "preact.umd.js.map"], "path": "libs/preact/dist/preact.umd.js", "cdnPath": "preact/dist/preact.umd" } }, + "preact/hooks": { "cdn": "3rdparty", "cwd": "node_modules/preact/hooks/dist", "debug": { - "src": [ - "hooks.umd.js", - "hooks.umd.js.map" - ], + "src": ["hooks.umd.js", "hooks.umd.js.map"], "path": "libs/preact/hooks/dist/hooks.umd.js", "cdnPath": "preact/hooks/dist/hooks.umd" }, "release": { - "src": [ - "hooks.umd.js", - "hooks.umd.js.map" - ], + "src": ["hooks.umd.js", "hooks.umd.js.map"], "path": "libs/preact/hooks/dist/hooks.umd.js", "cdnPath": "preact/hooks/dist/hooks.umd" } }, + "preact/compat": { "cdn": "3rdparty", "cwd": "node_modules/preact/compat/dist", @@ -259,52 +270,57 @@ "cdnPath": "preact/compat/dist/compat.umd" } }, + "preact/jsx-runtime": { + "cdn": "3rdparty", + "cwd": "node_modules/preact/jsx-runtime/dist", + "debug": { + "src": ["jsxRuntime.umd.js", "jsxRuntime.umd.js.map"], + "path": "libs/preact/jsx-runtime/dist/jsxRuntime.umd.js", + "cdnPath": "preact/jsx-runtime/dist/jsxRuntime.umd" + }, + "release": { + "src": ["jsxRuntime.umd.js", "jsxRuntime.umd.js.map"], + "path": "libs/preact/jsx-runtime/dist/jsxRuntime.umd.js", + "cdnPath": "preact/jsx-runtime/dist/jsxRuntime.umd" + } + }, + "preact/debug": { "cdn": "3rdparty", "cwd": "node_modules/preact/debug/dist", "debug": { - "src": [ - "debug.umd.js", - "debug.umd.js.map" - ], + "src": ["debug.umd.js", "debug.umd.js.map"], "path": "libs/preact/debug/dist/debug.umd.js", "cdnPath": "preact/debug/dist/debug.umd" }, "release": { - "src": [ - "debug.umd.js", - "debug.umd.js.map" - ], + "src": ["debug.umd.js", "debug.umd.js.map"], "path": "libs/preact/debug/dist/debug.umd.js", "cdnPath": "preact/debug/dist/debug.umd" } }, + "preact/devtools": { "cdn": "3rdparty", "cwd": "node_modules/preact/devtools/dist", "debug": { - "src": [ - "devtools.umd.js", - "devtools.umd.js.map" - ], + "src": ["devtools.umd.js", "devtools.umd.js.map"], "path": "libs/preact/devtools/dist/devtools.umd.js", "cdnPath": "preact/devtools/dist/devtools.umd" }, "release": { - "src": [ - "devtools.umd.js", - "devtools.umd.js.map" - ], + "src": ["devtools.umd.js", "devtools.umd.js.map"], "path": "libs/preact/devtools/dist/devtools.umd.js", "cdnPath": "preact/devtools/dist/devtools.umd" } }, + "proj4": { "cdn": "3rdparty", - "cwd": "proj4/dist", + "cwd": "node_modules/proj4/dist", "debug": { - "src": "proj4-src.js", - "path": "libs/proj4js/dist/proj4-src.js", + "src": "proj4-src.js", + "path": "libs/proj4js/dist/proj4-src.js", "cdnPath": "proj4js/dist/proj4" }, "release": { @@ -313,63 +329,55 @@ "cdnPath": "proj4js/dist/proj4" } }, + "css": { "cdn": "3rdparty", "cwd": "node_modules/require-css", "debug": { - "src": [ - "css.js", - "css-builder.js", - "normalize.js" - ], + "src": ["css.js", "css-builder.js", "normalize.js"], "path": "libs/require-css/css.js", "cdnPath": "require-css/css" }, "release": { - "src": [ - "css.min.js", - "css-builder.js", - "normalize.js" - ], + "src": ["css.min.js", "css-builder.js", "normalize.js"], "path": "libs/require-css/css.min.js", "cdnPath": "require-css/css.min" } }, + "ojcss": { "cdn": "jet", "cwd": "node_modules/@oracle/oraclejet/dist/js/libs/oj", "debug": { - "src": [ - "debug/ojcss.js" - ], - "path": "libs/oj/v#{version}", + "src": ["debug/ojcss.js"], + "path": "libs/oj/#{version}", "pathSuffix": "/debug/ojcss'", "cdnPath": "ojcss" }, "release": { - "src": [ - "min/ojcss.js" - ], - "path": "libs/oj/v#{version}", + "src": ["min/ojcss.js"], + "path": "libs/oj/#{version}", "pathSuffix": "/min/ojcss'", "cdnPath": "ojcss" } }, + "ojs/ojcss": { "cdn": "jet", "cwd": "node_modules/@oracle/oraclejet/dist/js/libs/oj", "debug": { "src": ["debug/ojcss.js"], - "path": "libs/oj/v#{version}", + "path": "libs/oj/#{version}", "pathSuffix": "/debug/ojcss'", "cdnPath": "ojcss" }, "release": { "src": ["min/ojcss.js"], - "path": "libs/oj/v#{version}", + "path": "libs/oj/#{version}", "pathSuffix": "/min/ojcss'", "cdnPath": "ojcss" } - } + } + } } diff --git a/test/templates/webTsApiTest/src/ts/jet-composites/my-pack/resources/component.json b/test/templates/webTsApiTest/src/ts/jet-composites/my-pack/resources/component.json index dc14ab4..88152a0 100644 --- a/test/templates/webTsApiTest/src/ts/jet-composites/my-pack/resources/component.json +++ b/test/templates/webTsApiTest/src/ts/jet-composites/my-pack/resources/component.json @@ -1,7 +1,7 @@ { "name": "resources", "version": "1.0.0", - "jetVersion": "^12.1.0", + "jetVersion": "^13.0.0", "type": "resource", "pack": "my-pack" } diff --git a/test/templates/webTsApiTest/tsconfig.json b/test/templates/webTsApiTest/tsconfig.json index 2b0af52..978685d 100644 --- a/test/templates/webTsApiTest/tsconfig.json +++ b/test/templates/webTsApiTest/tsconfig.json @@ -5,10 +5,10 @@ "target": "es6", "module": "amd", "moduleResolution": "node", - "jsx": "react", - "jsxFactory": "h", - "lib": [ - "es2015", + "jsx": "react-jsx", + "jsxImportSource": "preact", + "lib": [ + "es2020", "dom", "esnext.asynciterable" ], diff --git a/test/templates/webpackLegacyTest/package.json b/test/templates/webpackLegacyTest/package.json index 56d3cc6..b5907e9 100644 --- a/test/templates/webpackLegacyTest/package.json +++ b/test/templates/webpackLegacyTest/package.json @@ -3,13 +3,15 @@ "version": "1.0.0", "description": "An Oracle JavaScript Extension Toolkit(JET) web app", "dependencies": { - "@oracle/oraclejet": "https://artifacthub-phx.oci.oraclecorp.com/ojet-dev-local/oracle-oraclejet-12.1.0.tgz" + "@oracle/oraclejet": "https://artifacthub-phx.oci.oraclecorp.com/ojet-dev-local/oracle-oraclejet-13.0.0.tgz" }, "devDependencies": { - "@oracle/oraclejet-tooling": "https://artifacthub-phx.oci.oraclecorp.com/ojet-dev-local/oracle-oraclejet-tooling-12.1.0.tgz", + "@oracle/oraclejet-tooling": "file:../../ojet-cli/node_modules/@oracle/oraclejet-tooling", "fs-extra": "^8.1.0", - "glob": "^7.1.1", - "typescript": "4.5.4", + "glob": "7.2.0", + "ts-creator": "~1.2.5", + "yargs-parser": "~13.1.2", + "typescript": "4.6.4", "underscore": "^1.10.2", "webpack": "~5.60.0", "text-loader": "^0.0.1", diff --git a/test/templates/webpackLegacyTest/path_mapping.json b/test/templates/webpackLegacyTest/path_mapping.json index 7f11862..7077d99 100644 --- a/test/templates/webpackLegacyTest/path_mapping.json +++ b/test/templates/webpackLegacyTest/path_mapping.json @@ -2,11 +2,12 @@ "use": "local", "cdns": { "jet": { - "prefix": "https://ci-cloud.us.oracle.com/jenkins/uitech/job/CDN_12.1.0/lastSuccessfulBuild/artifact/cdn/v12.1.0/default/js", - "css": "https://ci-cloud.us.oracle.com/jenkins/uitech/job/CDN_12.1.0/lastSuccessfulBuild/artifact/cdn/v12.1.0/default/css", + "prefix": "https://ci-cloud.us.oracle.com/jenkins/uitech/job/CDN_master/lastSuccessfulBuild/artifact/cdn/13.0.0/default/js", + "css": "https://ci-cloud.us.oracle.com/jenkins/uitech/job/CDN_master/lastSuccessfulBuild/artifact/cdn/13.0.0/default/css", + "csspreact": "https://ci-cloud.us.oracle.com/jenkins/uitech/job/CDN_master/lastSuccessfulBuild/artifact/cdn/13.0.0/3rdparty/oraclejet-preact/amd", "config": "bundles-config.js" }, - "3rdparty": "https://ci-cloud.us.oracle.com/jenkins/uitech/job/CDN_12.1.0/lastSuccessfulBuild/artifact/cdn/v12.1.0/3rdparty" + "3rdparty": "https://ci-cloud.us.oracle.com/jenkins/uitech/job/CDN_master/lastSuccessfulBuild/artifact/cdn/13.0.0/3rdparty" }, "libs": { "knockout": { @@ -49,7 +50,7 @@ "vendor/jquery-color/jquery.color.js" ], "path": "libs/jquery/jqueryui-amd-#{version}", - "cdnPath": "jquery/jqueryui-amd-1.13.0" + "cdnPath": "jquery/jqueryui-amd-1.12.1" }, "release": { "src": [ @@ -60,7 +61,7 @@ "vendor/jquery-color/jquery.color.js" ], "path": "libs/jquery/jqueryui-amd-#{version}.min", - "cdnPath": "jquery/jqueryui-amd-1.13.0.min" + "cdnPath": "jquery/jqueryui-amd-1.12.1.min" } }, "hammerjs": { @@ -98,7 +99,7 @@ "src": [ "debug*/**" ], - "path": "libs/oj/v#{version}", + "path": "libs/oj/#{version}", "pathSuffix": "/debug'", "cdnPath": "" }, @@ -106,7 +107,7 @@ "src": [ "min*/**" ], - "path": "libs/oj/v#{version}", + "path": "libs/oj/#{version}", "pathSuffix": "/min'", "cdnPath": "" } @@ -116,12 +117,12 @@ "cwd": "node_modules/@oracle/oraclejet/dist/js/libs/oj", "debug": { "src": "ojL10n.js", - "path": "libs/oj/v#{version}/ojL10n.js", + "path": "libs/oj/#{version}/ojL10n.js", "cdnPath": "ojL10n" }, "release": { "src": "ojL10n.js", - "path": "libs/oj/v#{version}/ojL10n.js", + "path": "libs/oj/#{version}/ojL10n.js", "cdnPath": "ojL10n" } }, @@ -132,17 +133,31 @@ "src": [ "**" ], - "path": "libs/oj/v#{version}/resources", + "path": "libs/oj/#{version}/resources", "cdnPath": "resources" }, "release": { "src": [ "**" ], - "path": "libs/oj/v#{version}/resources", + "path": "libs/oj/#{version}/resources", "cdnPath": "resources" } }, + "@oracle/oraclejet-preact": { + "cdn": "3rdparty", + "cwd": "node_modules/@oracle/oraclejet/dist/js/libs/oraclejet-preact/amd", + "debug": { + "src": ["**"], + "path": "libs/oraclejet-preact/amd", + "cdnPath": "oraclejet-preact/amd" + }, + "release": { + "src": ["**"], + "path": "libs/oraclejet-preact/amd", + "cdnPath": "oraclejet-preact/amd" + } + }, "persist": { "cdn": "3rdparty", "cwd": "node_modules/@oracle/oraclejet/dist/js/libs/persist", @@ -342,7 +357,7 @@ "src": [ "debug/ojcss.js" ], - "path": "libs/oj/v#{version}", + "path": "libs/oj/#{version}", "pathSuffix": "/debug/ojcss'", "cdnPath": "ojcss" }, @@ -350,7 +365,7 @@ "src": [ "min/ojcss.js" ], - "path": "libs/oj/v#{version}", + "path": "libs/oj/#{version}", "pathSuffix": "/min/ojcss'", "cdnPath": "ojcss" } @@ -362,7 +377,7 @@ "src": [ "debug/ojcss.js" ], - "path": "libs/oj/v#{version}", + "path": "libs/oj/#{version}", "pathSuffix": "/debug/ojcss'", "cdnPath": "ojcss" }, @@ -370,7 +385,7 @@ "src": [ "min/ojcss.js" ], - "path": "libs/oj/v#{version}", + "path": "libs/oj/#{version}", "pathSuffix": "/min/ojcss'", "cdnPath": "ojcss" } diff --git a/test/templates/webpackLegacyTest/src/main.js b/test/templates/webpackLegacyTest/src/main.js index 2ba0895..16fac29 100644 --- a/test/templates/webpackLegacyTest/src/main.js +++ b/test/templates/webpackLegacyTest/src/main.js @@ -31,9 +31,9 @@ */ // injector:mainReleasePaths { - 'ojs': 'libs/oj/v12.1.0/debug', - 'ojL10n': 'libs/oj/v12.1.0/ojL10n', - 'ojtranslations': 'libs/oj/v12.1.0/resources', + 'ojs': 'libs/oj/13.0.0/debug', + 'ojL10n': 'libs/oj/13.0.0/ojL10n', + 'ojtranslations': 'libs/oj/13.0.0/resources', 'knockout': 'libs/knockout/knockout-3.5.1.debug', 'jquery': 'libs/jquery/jquery-3.6.0', 'jqueryui-amd': 'libs/jquery/jqueryui-amd-1.13.0', @@ -50,7 +50,7 @@ 'proj4': 'libs/proj4js/dist/proj4-src', 'touchr': 'libs/touchr/touchr' , - 'chai': 'libs/chai/chai-4.3.4' + 'chai': 'libs/chai/chai-4.3.6' } // endinjector } diff --git a/test/templates/webpackLegacyTest/tsconfig.json b/test/templates/webpackLegacyTest/tsconfig.json index bda8891..d144961 100644 --- a/test/templates/webpackLegacyTest/tsconfig.json +++ b/test/templates/webpackLegacyTest/tsconfig.json @@ -5,10 +5,10 @@ "target": "es6", "module": "amd", "moduleResolution": "node", - "jsx": "react", - "jsxFactory": "h", + "jsx": "react-jsx", + "jsxImportSource": "preact", "lib": [ - "es2015", + "es2020", "dom", "esnext.asynciterable" ], diff --git a/test/themeTest.js b/test/themeTest.js index 28467db..340704a 100644 --- a/test/themeTest.js +++ b/test/themeTest.js @@ -10,6 +10,7 @@ const path = require('path'); const util = require('./util'); const appDir = util.getAppDir(util.THEME_APP_NAME); +const THEME_NAME = 'testTheme'; describe('PCSS Theme Test', () => { before(async () => { @@ -50,10 +51,63 @@ describe('PCSS Theme Test', () => { assert.ok(/node-sass is not installed. To install it, run: ojet add sass./.test(result.stdout), true, result.stdout); }); + it('Should not create a custom theme before running ojet add sass or ojet add theming.', async () => { + const result = await util.execCmd(`${util.OJET_APP_COMMAND} create theme ${THEME_NAME}`, { cwd: appDir }, true); + // "Please run 'ojet add sass'..." is part of the message that includes the suggestion to run ojet add theming, if preferred: + assert.ok(/Please run 'ojet add sass' to configure your projects for SASS processing./.test(result.stdout), result.stdout); + }); + + it('Should throw a warning on creating a custom theme that defaults to alta as base-theme.', async () => { + await util.execCmd(`${util.OJET_APP_COMMAND} add sass`, { cwd: appDir }, true); + const result = await util.execCmd(`${util.OJET_APP_COMMAND} create theme ${THEME_NAME}`, { cwd: appDir }, true); + assert.ok(/The created theme defaults to alta as base-theme, which is deprecated./.test(result.stdout), result.stdout); + }); + it('Should add theming to enable node-sass, postcss-custom-theme, postcss-calc, autoprefixer', async () => { const result = await util.execCmd(`${util.OJET_APP_COMMAND} add theming`, { cwd: appDir }); assert.equal(/add pcss complete/.test(result.stdout), true, result.stdout); }); + + it('Should set default theme to redwood-notag and refer to its css file in index.html on build', async () => { + const { pathToApp, pathToIndexHtml } = util.getAppPathData(util.THEME_APP_NAME); + util.setDefaultTheme('redwood-notag', pathToApp); + const result = await util.execCmd(`${util.OJET_APP_COMMAND} build`, {cwd: appDir}); + assert.equal(util.buildSuccess(result.stdout), true, result.error); + const regex = new RegExp('href="css\/redwood\/web\/redwood-notag.css"', 'gm'); + const hasRedwoodNotag = util.checkThemingLink(regex, pathToIndexHtml); + assert.ok(hasRedwoodNotag, "The index.html file does not have a link to redwood-notag.css."); + }); + + it('Should set default theme to redwood-notag and refer to its css file in index.html on build release', async () => { + const { pathToApp, pathToIndexHtml } = util.getAppPathData(util.THEME_APP_NAME); + util.setDefaultTheme('redwood-notag', pathToApp); + const result = await util.execCmd(`${util.OJET_APP_COMMAND} build --release`, {cwd: appDir}); + assert.equal(util.buildSuccess(result.stdout), true, result.error); + const regex = new RegExp('href="css\/redwood\/web\/redwood-notag.min.css"', 'gm'); + const hasRedwoodNotagMin = util.checkThemingLink(regex, pathToIndexHtml); + assert.ok(hasRedwoodNotagMin, "The index.html file does not have a link to redwood-notag.min.css."); + }); + + describe ('Preact Theme Test', () => { + it('Should have preact theme files in staging and the referred links in index.html on build with redwood theme', async () => { + const { pathToIndexHtml } = util.getAppPathData(util.THEME_APP_NAME); + const result = await util.execCmd(`${util.OJET_APP_COMMAND} build --theme=redwood`, {cwd: appDir}); + assert.equal(util.buildSuccess(result.stdout), true, result.error); + const regexRedwood = new RegExp(`href="css\/theme-redwood\/${util.getJetVersion(util.THEME_APP_NAME)}\/web\/theme.css"`, 'gm'); + const hasLinkToThemeRedwood = util.checkThemingLink(regexRedwood, pathToIndexHtml); + assert.ok(hasLinkToThemeRedwood, "The index.html file does not have a link to theme-redwood.css."); + }); + + it('Should not have preact theme files in staging and the referred links in index.html on build with stable theme', async () => { + const { pathToIndexHtml } = util.getAppPathData(util.THEME_APP_NAME); + const result = await util.execCmd(`${util.OJET_APP_COMMAND} build --theme=stable`, {cwd: appDir}); + assert.equal(util.buildSuccess(result.stdout), true, result.error); + const regexRedwood = new RegExp(`href="css\/theme-redwood\/${util.getJetVersion(util.THEME_APP_NAME)}\/web\/theme.css"`, 'gm'); + const hasLinkToThemeRedwood = util.checkThemingLink(regexRedwood, pathToIndexHtml); + assert.ok(!hasLinkToThemeRedwood, "The index.html has a link to theme-redwood.css."); + }); + }); + describe('Create and compile pcss theme', () => { it('Should create theme with basetheme stable after add theming', async () => { const removetheme = path.resolve(appDir, 'src/themes'); @@ -88,16 +142,20 @@ describe('PCSS Theme Test', () => { const pathToRedwoodInStaging = path.join(pathToApp, stagingFolder, stylesFolder, 'redwood', util.getJetVersion(util.THEME_APP_NAME),'web'); const pathToRedwoodInNodeModules = path.join(pathToNodeModules, '@oracle/oraclejet/dist/css/redwood'); const everyExpectedEntryIsPresent = fs.readdirSync(pathToRedwoodInNodeModules).every(dirEntry => { - // oj-redwood.css is renamed to redwood.css in the staging location so - // we have to update the existence check: + // oj-redwood.css, oj-redwood.min.css (and their minified versions) are renamed to redwood.css + // and redwood-notag.css in the staging location so we have to update the existence check: if (dirEntry === 'oj-redwood.css') { return fs.existsSync(path.join(pathToRedwoodInStaging, 'redwood.css')); } - // Similarly, oj-redwood-min.css is renamed to redwood.min.css in the staging - // location so we have to update the existence check: else if (dirEntry === 'oj-redwood-min.css') { return fs.existsSync(path.join(pathToRedwoodInStaging, 'redwood.min.css')); } + else if (dirEntry === 'oj-redwood-notag.css') { + return fs.existsSync(path.join(pathToRedwoodInStaging, 'redwood-notag.css')); + } + else if (dirEntry === 'oj-redwood-notag-min.css') { + return fs.existsSync(path.join(pathToRedwoodInStaging, 'redwood-notag.min.css')); + } return fs.existsSync(path.join(pathToRedwoodInStaging, dirEntry)); }); assert.ok(everyExpectedEntryIsPresent, "Not all redwood files are present in staging location."); diff --git a/test/util/index.js b/test/util/index.js index f56728e..539de1a 100644 --- a/test/util/index.js +++ b/test/util/index.js @@ -64,8 +64,7 @@ const WEBPACK_DEPENDENCIES = [ '@prefresh/babel-plugin', 'webpack-merge', 'compression-webpack-plugin', - 'mini-css-extract-plugin', - 'zlib' + 'mini-css-extract-plugin' ]; const COMPONENT_JSON_DEPENDENCIES_TOKEN = '@dependencies@'; const COMPONENT_JSON = 'component.json'; @@ -78,12 +77,15 @@ module.exports = { testDir: td, APP_NAME: 'webJsTest', HYBRID_APP_NAME: 'hybridJsTest', + OJC_APP_NAME: 'ojcTest', TS_APP_NAME: 'webTsTest', THEME_APP_NAME: 'webJsThemeTest', PWA_APP_NAME: 'webJsPwaTest', API_APP_NAME: 'webTsApiTest', VDOM_APP_NAME: 'vdomTest', WEBPACK_APP_NAME: 'webpackTest', + WEBPACK_JS_APP_NAME: 'webpackJsTest', + WEBPACK_TS_APP_NAME: 'webpackTsTest', WEBPACK_LEGACY_APP_NAME: 'webpackLegacyTest', COMPONENT_APP_NAME, COMPONENT_TS_APP_NAME, @@ -292,6 +294,19 @@ module.exports = { fs.writeFileSync(filePath, customHookContent); }, + checkThemingLink: function _checkThemingLink(expression, pathToIndexHtml) { + const indexHtmlContent = fs.readFileSync(pathToIndexHtml); + const regex = new RegExp(expression, 'gm'); + return regex.test(indexHtmlContent); + }, + + setDefaultTheme: function _setDefaultTheme(theme, appPath) { + const pathToConfigJSON = path.join(appPath, this.ORACLEJET_CONFIG_JSON); + const configJSON = fs.readJSONSync(pathToConfigJSON); + configJSON.defaultTheme = theme; + fs.writeJSONSync(pathToConfigJSON, configJSON); + }, + getHooksPathAndContent: function _getHooksPathAndContent(appName) { const { pathToAppHooks } = this.getAppPathData(appName); const beforePackageHookPath = path.join(pathToAppHooks, `before_component_package.js`); diff --git a/test/vdomTest.js b/test/vdomTest.js index 1116f2b..77fcc2b 100644 --- a/test/vdomTest.js +++ b/test/vdomTest.js @@ -111,6 +111,20 @@ describe('VDOM Test', () => { assert.ok(false); } }); + it('should have injected preact debug', async () => { + const {pathToApp, stagingFolder} = util.getAppPathData(util.VDOM_APP_NAME); + const pathToIndexTs = path.join(pathToApp, stagingFolder, 'index.ts'); + const tsContent = fs.readFileSync(pathToIndexTs, { encoding: 'utf-8' }); + const regex = /import 'preact\/debug'/gm; + assert(regex.exec(tsContent), "import 'preact/debug' not found"); + }); + it('should have injected preact theming', async () => { + const {pathToApp, stagingFolder} = util.getAppPathData(util.VDOM_APP_NAME); + const pathToIndexHtml = path.join(pathToApp, stagingFolder, 'index.html'); + const htmlContent = fs.readFileSync(pathToIndexHtml, { encoding: 'utf-8' }); + const regex = /styles\/theme-redwood/gm; + assert(regex.exec(htmlContent), "styles/theme-redwood not found"); + }); } }); diff --git a/test/webTest.js b/test/webTest.js index c5fb9f2..65c21ad 100644 --- a/test/webTest.js +++ b/test/webTest.js @@ -24,7 +24,6 @@ describe('Web Test', () => { const platform = util.getPlatform(process.env.OS); util.removeAppDir(util.APP_NAME); - util.removeAppDir(util.TEST_DIR); // Scaffold a basic web app let result = await util.execCmd(`${util.OJET_COMMAND} create ${util.APP_NAME} --norestore=true`, { cwd: util.testDir }); diff --git a/test/webpackTest.js b/test/webpackTest.js index 6a2a557..1f00380 100644 --- a/test/webpackTest.js +++ b/test/webpackTest.js @@ -10,6 +10,23 @@ const path = require('path'); const Ojet = require('../ojet'); const util = require('./util'); +function checkWebIndexHTML(appName, token) { + const regex = new RegExp(token); + const { pathToIndexHtml } = util.getAppPathData(appName); + const indexHtmlContent = fs.readFileSync(pathToIndexHtml, { encoding: 'utf-8' }); + const hasMatchedPattern = regex.test(indexHtmlContent); + return {pathToIndexHtml, hasMatchedPattern}; +} + +function checkSrcIndexHTML(appName, token){ + const regex = new RegExp(token); + const appDir = util.getAppDir(appName); + const pathToIndexHtml = path.join(appDir, 'src', 'index.html'); + const indexHtmlContent = fs.readFileSync(pathToIndexHtml, { encoding: 'utf-8' }); + const hasMatchedPattern = regex.test(indexHtmlContent); + return {pathToIndexHtml, hasMatchedPattern}; +} + describe('Webpack Test', () => { before(async () => { if (!util.noScaffold()) { @@ -60,22 +77,154 @@ describe('Webpack Test', () => { assert.ok(false, `Error running ojet.execute with ${executeOptions}`); } } + if (!util.noScaffold()) { + // Create end-to-end webpack app + util.removeAppDir(util.WEBPACK_JS_APP_NAME); + // Scaffold vdomTest application using API + const ojet = new Ojet({ cwd: util.testDir, logs: false }); + let executeOptions = {}; + try { + executeOptions = { + task: 'create', + parameters: [util.WEBPACK_JS_APP_NAME], + options: { + template: 'basic', + vdom: false, + webpack: true + } + }; + await ojet.execute(executeOptions); + + // Change the port in ojet.config.js + const testDir = util.getAppDir(util.WEBPACK_JS_APP_NAME); + const fileName = path.resolve(testDir, 'ojet.config.js'); + let configRead = fs.readFileSync(fileName, 'utf-8'); + configRead = configRead.replace('return config;', 'if (config && config.devServer) config.devServer.port = 8001; return config;'); + // write it back out + fs.unlinkSync(fileName); + fs.writeFileSync(fileName, configRead); + + assert.ok(true); + } catch (e) { + console.log(e); + assert.ok(false, `Error running ojet.execute with ${executeOptions}`); + } + } + if (!util.noScaffold()) { + // Create end-to-end webpack app + util.removeAppDir(util.WEBPACK_TS_APP_NAME); + // Scaffold vdomTest application using API + const ojet = new Ojet({ cwd: util.testDir, logs: false }); + let executeOptions = {}; + try { + executeOptions = { + task: 'create', + parameters: [util.WEBPACK_TS_APP_NAME], + options: { + template: 'navbar-ts', + typescript: true, + webpack: true + } + }; + await ojet.execute(executeOptions); + + // Change the port in ojet.config.js + const testDir = util.getAppDir(util.WEBPACK_JS_APP_NAME); + const fileName = path.resolve(testDir, 'ojet.config.js'); + let configRead = fs.readFileSync(fileName, 'utf-8'); + configRead = configRead.replace('return config;', 'if (config && config.devServer) config.devServer.port = 8002; return config;'); + // write it back out + fs.unlinkSync(fileName); + fs.writeFileSync(fileName, configRead); + + assert.ok(true); + } catch (e) { + console.log(e); + assert.ok(false, `Error running ojet.execute with ${executeOptions}`); + } + } }); describe('Webpack', () => { describe('Scaffold', () => { - it('should check that webpack and its dependencies are listed in package.json', () => { + it('should check that webpack and its dependencies are listed in package.json - vdom app', () => { const { pathToApp } = util.getAppPathData(util.WEBPACK_APP_NAME); const packageJson = fs.readJsonSync(path.join(pathToApp, 'package.json')); util.WEBPACK_DEPENDENCIES.forEach((dependency) => { assert.ok(packageJson.devDependencies[dependency], `${dependency} not installed`); }); }); - it(`should check that ${util.OJET_CONFIG_JS} was added`, () => { + it('should check that webpack and its dependencies are listed in package.json - js app', () => { + const { pathToApp } = util.getAppPathData(util.WEBPACK_JS_APP_NAME); + const packageJson = fs.readJsonSync(path.join(pathToApp, 'package.json')); + util.WEBPACK_DEPENDENCIES.forEach((dependency) => { + assert.ok(packageJson.devDependencies[dependency], `${dependency} not installed`); + }); + }); + it('should check that webpack and its dependencies are listed in package.json - ts app', () => { + const { pathToApp } = util.getAppPathData(util.WEBPACK_TS_APP_NAME); + const packageJson = fs.readJsonSync(path.join(pathToApp, 'package.json')); + util.WEBPACK_DEPENDENCIES.forEach((dependency) => { + assert.ok(packageJson.devDependencies[dependency], `${dependency} not installed`); + }); + }); + it(`should check that ${util.OJET_CONFIG_JS} was added in vdom app`, () => { const { pathToApp } = util.getAppPathData(util.WEBPACK_APP_NAME); const pathToOjetConfigJs = path.join(pathToApp, util.OJET_CONFIG_JS); assert.ok(fs.existsSync(pathToOjetConfigJs), `${util.OJET_CONFIG_JS} not in application`); }); + it(`should check that ${util.OJET_CONFIG_JS} was added in js app`, () => { + const { pathToApp } = util.getAppPathData(util.WEBPACK_JS_APP_NAME); + const pathToOjetConfigJs = path.join(pathToApp, util.OJET_CONFIG_JS); + assert.ok(fs.existsSync(pathToOjetConfigJs), `${util.OJET_CONFIG_JS} not in application`); + }); + it(`should check that ${util.OJET_CONFIG_JS} was added in ts app`, () => { + const { pathToApp } = util.getAppPathData(util.WEBPACK_TS_APP_NAME); + const pathToOjetConfigJs = path.join(pathToApp, util.OJET_CONFIG_JS); + assert.ok(fs.existsSync(pathToOjetConfigJs), `${util.OJET_CONFIG_JS} not in application`); + }); }); +/* describe('Serve', () => { + if (!util.noServe()) { + describe('serve a vdom app with webpack', () => { + it('should serve with nobuild', async () => { + let result; + try { + result = await util.execCmd(`${util.OJET_APP_COMMAND} serve web --no-build`, { cwd: util.getAppDir(util.WEBPACK_APP_NAME), maxBuffer: 1024 * 20000, timeout:30000, killSignal:'SIGTERM' }, true); + assert.equal(/webpack \d+.\d+.\d+ compiled/i.test(result.stdout), true, result.stdout); + } finally { + if (result) { + result.process.kill(); + } + } + }); + }); + describe('serve a js app with webpack', () => { + it('should serve with nobuild', async () => { + let result; + try { + result = await util.execCmd(`${util.OJET_APP_COMMAND} serve web --no-build`, { cwd: util.getAppDir(util.WEBPACK_JS_APP_NAME), maxBuffer: 1024 * 20000, timeout:30000, killSignal:'SIGTERM' }, true); + assert.equal(/webpack \d+.\d+.\d+ compiled/i.test(result.stdout), true, result.stdout); + } finally { + if (result) { + result.process.kill(); + } + } + }); + }); + describe('serve a ts app with webpack', () => { + it('should serve with nobuild', async () => { + try { + result = await util.execCmd(`${util.OJET_APP_COMMAND} serve web --no-build`, { cwd: util.getAppDir(util.WEBPACK_TS_APP_NAME), maxBuffer: 1024 * 20000, timeout:30000, killSignal:'SIGTERM' }, true); + assert.equal(/webpack \d+.\d+.\d+ compiled/i.test(result.stdout), true, result.stdout); + } finally { + if (result) { + result.process.kill(); + } + } + }); + }); + } + });*/ describe('Build (Debug)', () => { it('should build in debug mode', async () => { const { pathToApp } = util.getAppPathData(util.WEBPACK_APP_NAME); @@ -87,9 +236,34 @@ describe('Webpack Test', () => { assert.ok(false); } }); + it(`should build in debug mode for ${util.WEBPACK_JS_APP_NAME}`, async () => { + const appDir = util.getAppDir(util.WEBPACK_JS_APP_NAME); + const result = await util.execCmd(`${util.OJET_APP_COMMAND} build`, { cwd: appDir }, true, true); + assert.equal(util.buildSuccess(result.stdout), true, result.error); + }); + it(`should build in debug mode for ${util.WEBPACK_TS_APP_NAME}`, async () => { + const appDir = util.getAppDir(util.WEBPACK_TS_APP_NAME); + const result = await util.execCmd(`${util.OJET_APP_COMMAND} build`, { cwd: appDir }, true, true); + assert.equal(util.buildSuccess(result.stdout), true, result.error); + }); + it(`${util.WEBPACK_JS_APP_NAME} should have oj-redwood-min.css link in index.html file in source`, () => { + const { pathToIndexHtml } = util.getAppPathData(util.WEBPACK_JS_APP_NAME); + const indexHtmlContent = fs.readFileSync(pathToIndexHtml, { encoding: 'utf-8' }); + const hasRedwoodTheme = //.test(indexHtmlContent); + assert.ok(hasRedwoodTheme, `src/index.html does not have link to Redwood theme`); + }); + it(`${util.WEBPACK_TS_APP_NAME} should have oj-redwood-min.css link in index.html file in source`, () => { + const { pathToIndexHtml } = util.getAppPathData(util.WEBPACK_TS_APP_NAME); + const indexHtmlContent = fs.readFileSync(pathToIndexHtml, { encoding: 'utf-8' }); + const hasRedwoodTheme = //.test(indexHtmlContent); + assert.ok(hasRedwoodTheme, `src/index.html does not have link to Redwood theme`); + }); }); describe('Build (Release)', () => { - it('should build in release mode', async () => { + const regexRedwood = //; + const regexInjectorThemeTag = /()?/; + const regexRedwoodTag = /()?/; + it('should build in release mode for a vdom app', async () => { const { pathToApp } = util.getAppPathData(util.WEBPACK_APP_NAME); const ojet = new Ojet({ cwd: pathToApp, logs: false }); try { @@ -99,18 +273,39 @@ describe('Webpack Test', () => { assert.ok(false); } }); - it('should have oj-redwood-min.css link in index.html file in staging', () => { - const { pathToIndexHtml } = util.getAppPathData(util.WEBPACK_APP_NAME); - const indexHtmlContent = fs.readFileSync(pathToIndexHtml, { encoding: 'utf-8' }); - const hasRedwoodTheme = //.test(indexHtmlContent); - assert.ok(hasRedwoodTheme, `${pathToIndexHtml} does not have link to Redwood theme`); - }); - it('should have tag in index.html in src folder', () => { - const appDir = util.getAppDir(util.WEBPACK_APP_NAME); - const pathToSrcIndexHTML = path.join(appDir, 'src', 'index.html'); - const indexHtmlContent = fs.readFileSync(pathToSrcIndexHTML, { encoding: 'utf-8' }); - const hasRedwoodThemeTag = /()?/.test(indexHtmlContent); - assert.ok(hasRedwoodThemeTag, `${pathToSrcIndexHTML} does not have Redwood theme tag in src/index.html`); + it(`should build in debug release for a js app`, async () => { + const appDir = util.getAppDir(util.WEBPACK_JS_APP_NAME); + const result = await util.execCmd(`${util.OJET_APP_COMMAND} build --release`, { cwd: appDir }, true, true); + assert.equal(util.buildSuccess(result.stdout), true, result.error); + }); + it(`should build in debug release for a ts app`, async () => { + const appDir = util.getAppDir(util.WEBPACK_TS_APP_NAME); + const result = await util.execCmd(`${util.OJET_APP_COMMAND} build --release`, { cwd: appDir }, true, true); + assert.equal(util.buildSuccess(result.stdout), true, result.error); + }); + it('should have oj-redwood-min.css link in index.html file in staging - vdom app', () => { + const { pathToIndexHtml, hasMatchedPattern} = checkWebIndexHTML(util.WEBPACK_APP_NAME, regexRedwood); + assert.ok(hasMatchedPattern, `${pathToIndexHtml} has a link to Redwood theme`); + }); + it('should have oj-redwood-min.css link in index.html file in staging - js app', () => { + const { pathToIndexHtml, hasMatchedPattern} = checkWebIndexHTML(util.WEBPACK_JS_APP_NAME, regexRedwood); + assert.ok(hasMatchedPattern, `${pathToIndexHtml} has a link to Redwood theme`); + }); + it('should have oj-redwood-min.css link in index.html file in staging - ts app', () => { + const { pathToIndexHtml, hasMatchedPattern} = checkWebIndexHTML(util.WEBPACK_TS_APP_NAME, regexRedwood); + assert.ok(hasMatchedPattern, `${pathToIndexHtml} has a link to Redwood theme`); + }); + it('should have tag in index.html in vdom app src folder', () => { + const { pathToIndexHtml, hasMatchedPattern} = checkSrcIndexHTML(util.WEBPACK_APP_NAME, regexRedwoodTag); + assert.ok(hasMatchedPattern, `${pathToIndexHtml} has a Redwood theme tag`); + }); + it('should have tag in index.html in js app src folder', () => { + const { pathToIndexHtml, hasMatchedPattern} = checkSrcIndexHTML(util.WEBPACK_JS_APP_NAME, regexInjectorThemeTag); + assert.ok(hasMatchedPattern, `${pathToIndexHtml} has an injector theme tag`); + }); + it('should have tag in index.html in ts app src folder', () => { + const { pathToIndexHtml, hasMatchedPattern} = checkSrcIndexHTML(util.WEBPACK_TS_APP_NAME, regexInjectorThemeTag); + assert.ok(hasMatchedPattern, `${pathToIndexHtml} has an injector theme tag`); }); }); });