Remove Karma and anything related
This commit is contained in:
parent
6df7c2bf66
commit
bd710a55b6
|
@ -36,6 +36,5 @@ exclude_paths:
|
|||
- webpack-report/
|
||||
- log/
|
||||
- backups/
|
||||
- coverage-javascript/
|
||||
- plugins/
|
||||
- file_hooks/
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
/builds/
|
||||
/coverage/
|
||||
/coverage-frontend/
|
||||
/coverage-javascript/
|
||||
/node_modules/
|
||||
/public/
|
||||
/tmp/
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -50,7 +50,6 @@ eslint-report.html
|
|||
/config/sidekiq.yml
|
||||
/config/registry.key
|
||||
/coverage/*
|
||||
/coverage-javascript/
|
||||
/db/*.sqlite3
|
||||
/db/*.sqlite3-journal
|
||||
/db/data.yml
|
||||
|
|
|
@ -152,8 +152,6 @@
|
|||
*.js @gitlab-org/maintainers/frontend
|
||||
/app/assets/ @gitlab-org/maintainers/frontend
|
||||
/ee/app/assets/ @gitlab-org/maintainers/frontend
|
||||
/spec/javascripts/ @gitlab-org/maintainers/frontend
|
||||
/ee/spec/javascripts/ @gitlab-org/maintainers/frontend
|
||||
/spec/frontend/ @gitlab-org/maintainers/frontend
|
||||
/ee/spec/frontend/ @gitlab-org/maintainers/frontend
|
||||
/spec/frontend_integration/ @gitlab-org/maintainers/frontend
|
||||
|
|
|
@ -184,41 +184,6 @@ eslint-as-if-foss:
|
|||
- *yarn-install
|
||||
- run_timed_command "yarn run lint:eslint:all"
|
||||
|
||||
.karma-base:
|
||||
extends: .frontend-test-base
|
||||
script:
|
||||
- export BABEL_ENV=coverage CHROME_LOG_FILE=chrome_debug.log
|
||||
- *yarn-install
|
||||
- run_timed_command "yarn karma"
|
||||
|
||||
karma:
|
||||
extends:
|
||||
- .karma-base
|
||||
- .frontend:rules:default-frontend-jobs
|
||||
needs:
|
||||
- job: "rspec frontend_fixture"
|
||||
- job: "rspec-ee frontend_fixture"
|
||||
optional: true
|
||||
coverage: '/^Statements *: (\d+\.\d+%)/'
|
||||
artifacts:
|
||||
name: coverage-javascript
|
||||
expire_in: 31d
|
||||
when: always
|
||||
paths:
|
||||
- chrome_debug.log
|
||||
- coverage-javascript/
|
||||
- tmp/tests/frontend/
|
||||
reports:
|
||||
junit: junit_karma.xml
|
||||
cobertura: coverage-javascript/cobertura-coverage.xml
|
||||
|
||||
karma-as-if-foss:
|
||||
extends:
|
||||
- .karma-base
|
||||
- .frontend:rules:default-frontend-jobs-as-if-foss
|
||||
- .as-if-foss
|
||||
needs: ["rspec frontend_fixture as-if-foss"]
|
||||
|
||||
.jest-base:
|
||||
extends: .frontend-test-base
|
||||
script:
|
||||
|
|
|
@ -12,7 +12,6 @@ pages:
|
|||
needs:
|
||||
- job: "rspec:coverage"
|
||||
- job: "coverage-frontend"
|
||||
- job: "karma"
|
||||
- job: "compile-production-assets"
|
||||
- job: "compile-storybook"
|
||||
# `update-tests-metadata` only runs on GitLab.com's EE schedules pipelines
|
||||
|
@ -27,7 +26,6 @@ pages:
|
|||
- mkdir -p public/$(dirname "$KNAPSACK_RSPEC_SUITE_REPORT_PATH") public/$(dirname "$FLAKY_RSPEC_SUITE_REPORT_PATH") public/$(dirname "$RSPEC_PACKED_TESTS_MAPPING_PATH")
|
||||
- mv coverage/ public/coverage-ruby/ || true
|
||||
- mv coverage-frontend/ public/coverage-frontend/ || true
|
||||
- mv coverage-javascript/ public/coverage-javascript/ || true
|
||||
- mv storybook/public public/storybook || true
|
||||
- cp .public/assets/application-*.css public/application.css || true
|
||||
- mv $KNAPSACK_RSPEC_SUITE_REPORT_PATH public/$KNAPSACK_RSPEC_SUITE_REPORT_PATH || true
|
||||
|
|
|
@ -564,7 +564,7 @@ export const addSelectOnFocusBehaviour = (selector = '.js-select-on-focus') => {
|
|||
*
|
||||
* Eg; roundOffFloat(3.141592, 3) = 3.142
|
||||
*
|
||||
* Refer to spec/javascripts/lib/utils/common_utils_spec.js for
|
||||
* Refer to spec/frontend/lib/utils/common_utils_spec.js for
|
||||
* more supported examples.
|
||||
*
|
||||
* @param {Float} number
|
||||
|
@ -581,7 +581,7 @@ export const roundOffFloat = (number, precision = 0) => {
|
|||
*
|
||||
* Eg; roundToNearestHalf(3.141592) = 3, roundToNearestHalf(3.41592) = 3.5
|
||||
*
|
||||
* Refer to spec/javascripts/lib/utils/common_utils_spec.js for
|
||||
* Refer to spec/frontend/lib/utils/common_utils_spec.js for
|
||||
* more supported examples.
|
||||
*
|
||||
* @param {Float} number
|
||||
|
@ -595,7 +595,7 @@ export const roundToNearestHalf = (num) => Math.round(num * 2).toFixed() / 2;
|
|||
*
|
||||
* Eg; roundDownFloat(3.141592, 3) = 3.141
|
||||
*
|
||||
* Refer to spec/javascripts/lib/utils/common_utils_spec.js for
|
||||
* Refer to spec/frontend/lib/utils/common_utils_spec.js for
|
||||
* more supported examples.
|
||||
*
|
||||
* @param {Float} number
|
||||
|
@ -645,7 +645,7 @@ export const NavigationType = {
|
|||
* matched with our query.
|
||||
*
|
||||
* You can learn more about behaviour of this method by referring to tests
|
||||
* within `spec/javascripts/lib/utils/common_utils_spec.js`.
|
||||
* within `spec/frontend/lib/utils/common_utils_spec.js`.
|
||||
*
|
||||
* @param {string} query String to search for
|
||||
* @param {object} searchSpace Object containing properties to search in for `query`
|
||||
|
|
|
@ -31,7 +31,7 @@ if (BABEL_ENV === 'coverage') {
|
|||
plugins.push([
|
||||
'babel-plugin-istanbul',
|
||||
{
|
||||
exclude: ['spec/javascripts/**/*', 'app/assets/javascripts/locale/**/app.js'],
|
||||
exclude: ['app/assets/javascripts/locale/**/app.js'],
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -1,203 +0,0 @@
|
|||
/* eslint-disable no-inner-declarations, no-param-reassign */
|
||||
const path = require('path');
|
||||
const chalk = require('chalk');
|
||||
const argumentsParser = require('commander');
|
||||
const glob = require('glob');
|
||||
const webpack = require('webpack');
|
||||
const IS_EE = require('./helpers/is_ee_env');
|
||||
const webpackConfig = require('./webpack.config');
|
||||
|
||||
const ROOT_PATH = path.resolve(__dirname, '..');
|
||||
const SPECS_PATH = /^(?:\.[\\/])?(ee[\\/])?spec[\\/]javascripts[\\/]/;
|
||||
|
||||
function exitError(message) {
|
||||
console.error(chalk.red(`\nError: ${message}\n`));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function exitWarn(message) {
|
||||
console.error(chalk.yellow(`\nWarn: ${message}\n`));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
function exit(message, isError = true) {
|
||||
const fn = isError ? exitError : exitWarn;
|
||||
|
||||
fn(message);
|
||||
}
|
||||
|
||||
// disable problematic options
|
||||
webpackConfig.entry = undefined;
|
||||
webpackConfig.mode = 'development';
|
||||
webpackConfig.optimization.nodeEnv = false;
|
||||
webpackConfig.optimization.runtimeChunk = false;
|
||||
webpackConfig.optimization.splitChunks = false;
|
||||
|
||||
// use quicker sourcemap option
|
||||
webpackConfig.devtool = 'cheap-inline-source-map';
|
||||
|
||||
// set BABEL_ENV to indicate when we're running code coverage
|
||||
webpackConfig.plugins.push(
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.BABEL_ENV': JSON.stringify(process.env.BABEL_ENV || process.env.NODE_ENV || null),
|
||||
}),
|
||||
);
|
||||
|
||||
const options = argumentsParser
|
||||
.option('--no-fail-on-empty-test-suite')
|
||||
.option(
|
||||
'-f, --filter-spec [filter]',
|
||||
'Filter run spec files by path. Multiple filters are like a logical OR.',
|
||||
(filter, memo) => {
|
||||
memo.push(filter, filter.replace(/\/?$/, '/**/*.js'));
|
||||
return memo;
|
||||
},
|
||||
[],
|
||||
)
|
||||
.parse(process.argv);
|
||||
|
||||
const specFilters = options.filterSpec;
|
||||
|
||||
const createContext = (specFiles, regex, suffix) => {
|
||||
const newContext = specFiles.reduce((context, file) => {
|
||||
const relativePath = file.replace(SPECS_PATH, '');
|
||||
context[file] = `./${relativePath}`;
|
||||
return context;
|
||||
}, {});
|
||||
|
||||
webpackConfig.plugins.push(
|
||||
new webpack.ContextReplacementPlugin(regex, path.join(ROOT_PATH, suffix), newContext),
|
||||
);
|
||||
};
|
||||
|
||||
if (specFilters.length) {
|
||||
// resolve filters
|
||||
let filteredSpecFiles = specFilters.map((filter) =>
|
||||
glob
|
||||
.sync(filter, {
|
||||
root: ROOT_PATH,
|
||||
matchBase: true,
|
||||
})
|
||||
.filter((filePath) => filePath.endsWith('spec.js')),
|
||||
);
|
||||
|
||||
// flatten
|
||||
filteredSpecFiles = Array.prototype.concat.apply([], filteredSpecFiles);
|
||||
|
||||
// remove duplicates
|
||||
filteredSpecFiles = [...new Set(filteredSpecFiles)];
|
||||
|
||||
if (filteredSpecFiles.length < 1) {
|
||||
const isError = options.failOnEmptyTestSuite;
|
||||
|
||||
exit('Your filter did not match any test files.', isError);
|
||||
}
|
||||
|
||||
if (!filteredSpecFiles.every((file) => SPECS_PATH.test(file))) {
|
||||
exitError('Test files must be located within /spec/javascripts.');
|
||||
}
|
||||
|
||||
const CE_FILES = filteredSpecFiles.filter((file) => !file.startsWith('ee'));
|
||||
createContext(CE_FILES, /[^e]{2}[\\/]spec[\\/]javascripts$/, 'spec/javascripts');
|
||||
|
||||
const EE_FILES = filteredSpecFiles.filter((file) => file.startsWith('ee'));
|
||||
createContext(EE_FILES, /ee[\\/]spec[\\/]javascripts$/, 'ee/spec/javascripts');
|
||||
}
|
||||
|
||||
// Karma configuration
|
||||
module.exports = (config) => {
|
||||
process.env.TZ = 'Etc/UTC';
|
||||
|
||||
const fixturesPath = `tmp/tests/frontend/fixtures${IS_EE ? '-ee' : ''}`;
|
||||
const staticFixturesPath = 'spec/frontend/fixtures/static';
|
||||
|
||||
const karmaConfig = {
|
||||
basePath: ROOT_PATH,
|
||||
browsers: ['ChromeHeadlessCustom'],
|
||||
client: {
|
||||
color: !process.env.CI,
|
||||
},
|
||||
customLaunchers: {
|
||||
ChromeHeadlessCustom: {
|
||||
base: 'ChromeHeadless',
|
||||
displayName: 'Chrome',
|
||||
flags: [
|
||||
// chrome cannot run in sandboxed mode inside a docker container unless it is run with
|
||||
// escalated kernel privileges (e.g. docker run --cap-add=CAP_SYS_ADMIN)
|
||||
'--no-sandbox',
|
||||
// https://bugs.chromium.org/p/chromedriver/issues/detail?id=2870
|
||||
'--enable-features=NetworkService,NetworkServiceInProcess',
|
||||
],
|
||||
},
|
||||
},
|
||||
frameworks: ['jasmine'],
|
||||
files: [
|
||||
{ pattern: 'spec/javascripts/test_bundle.js', watched: false },
|
||||
{ pattern: `${fixturesPath}/**/*`, included: false },
|
||||
{ pattern: `${staticFixturesPath}/**/*`, included: false },
|
||||
],
|
||||
proxies: {
|
||||
'/fixtures/': `/base/${fixturesPath}/`,
|
||||
'/fixtures/static/': `/base/${staticFixturesPath}/`,
|
||||
},
|
||||
preprocessors: {
|
||||
'spec/javascripts/**/*.js': ['webpack', 'sourcemap'],
|
||||
'ee/spec/javascripts/**/*.js': ['webpack', 'sourcemap'],
|
||||
},
|
||||
reporters: ['mocha'],
|
||||
webpack: webpackConfig,
|
||||
webpackMiddleware: { stats: 'errors-only' },
|
||||
plugins: [
|
||||
'karma-chrome-launcher',
|
||||
'karma-coverage-istanbul-reporter',
|
||||
'karma-jasmine',
|
||||
'karma-junit-reporter',
|
||||
'karma-mocha-reporter',
|
||||
'karma-sourcemap-loader',
|
||||
'karma-webpack',
|
||||
],
|
||||
};
|
||||
|
||||
if (process.env.CI) {
|
||||
karmaConfig.reporters.push('junit');
|
||||
karmaConfig.junitReporter = {
|
||||
outputFile: 'junit_karma.xml',
|
||||
useBrowserName: false,
|
||||
};
|
||||
} else {
|
||||
// ignore 404s in local environment because we are not fixing them and they bloat the log
|
||||
function ignore404() {
|
||||
return (request, response /* next */) => {
|
||||
response.writeHead(404);
|
||||
return response.end('NOT FOUND');
|
||||
};
|
||||
}
|
||||
|
||||
karmaConfig.middleware = ['ignore-404'];
|
||||
karmaConfig.plugins.push({
|
||||
'middleware:ignore-404': ['factory', ignore404],
|
||||
});
|
||||
}
|
||||
|
||||
if (process.env.BABEL_ENV === 'coverage' || process.env.NODE_ENV === 'coverage') {
|
||||
karmaConfig.reporters.push('coverage-istanbul');
|
||||
karmaConfig.coverageIstanbulReporter = {
|
||||
reports: ['html', 'text-summary', 'cobertura'],
|
||||
dir: 'coverage-javascript/',
|
||||
subdir: '.',
|
||||
fixWebpackSourcePaths: true,
|
||||
};
|
||||
karmaConfig.browserNoActivityTimeout = 60000; // 60 seconds
|
||||
}
|
||||
|
||||
if (process.env.DEBUG) {
|
||||
karmaConfig.logLevel = config.LOG_DEBUG;
|
||||
process.env.CHROME_LOG_FILE = process.env.CHROME_LOG_FILE || 'chrome_debug.log';
|
||||
}
|
||||
|
||||
if (process.env.CHROME_LOG_FILE) {
|
||||
karmaConfig.customLaunchers.ChromeHeadlessCustom.flags.push('--enable-logging', '--v=1');
|
||||
}
|
||||
|
||||
config.set(karmaConfig);
|
||||
};
|
|
@ -154,7 +154,6 @@ const alias = {
|
|||
vendor: path.join(ROOT_PATH, 'vendor/assets/javascripts'),
|
||||
vue$: 'vue/dist/vue.esm.js',
|
||||
jquery$: 'jquery/dist/jquery.slim.js',
|
||||
spec: path.join(ROOT_PATH, 'spec/javascripts'),
|
||||
jest: path.join(ROOT_PATH, 'spec/frontend'),
|
||||
shared_queries: path.join(ROOT_PATH, 'app/graphql/queries'),
|
||||
|
||||
|
@ -178,7 +177,6 @@ if (IS_EE) {
|
|||
ee_empty_states: path.join(ROOT_PATH, 'ee/app/views/shared/empty_states'),
|
||||
ee_icons: path.join(ROOT_PATH, 'ee/app/views/shared/icons'),
|
||||
ee_images: path.join(ROOT_PATH, 'ee/app/assets/images'),
|
||||
ee_spec: path.join(ROOT_PATH, 'ee/spec/javascripts'),
|
||||
ee_jest: path.join(ROOT_PATH, 'ee/spec/frontend'),
|
||||
ee_else_ce: path.join(ROOT_PATH, 'ee/app/assets/javascripts'),
|
||||
});
|
||||
|
@ -191,7 +189,6 @@ if (IS_JH) {
|
|||
jh_empty_states: path.join(ROOT_PATH, 'jh/app/views/shared/empty_states'),
|
||||
jh_icons: path.join(ROOT_PATH, 'jh/app/views/shared/icons'),
|
||||
jh_images: path.join(ROOT_PATH, 'jh/app/assets/images'),
|
||||
jh_spec: path.join(ROOT_PATH, 'jh/spec/javascripts'),
|
||||
jh_jest: path.join(ROOT_PATH, 'jh/spec/frontend'),
|
||||
jh_else_ce: path.join(ROOT_PATH, 'jh/app/assets/javascripts'),
|
||||
});
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
# rubocop:disable Style/SignalException
|
||||
|
||||
def get_karma_files(files)
|
||||
files.select do |file|
|
||||
file.start_with?('ee/spec/javascripts', 'spec/javascripts') &&
|
||||
file.end_with?('_spec.js') &&
|
||||
!file.end_with?('browser_spec.js')
|
||||
end
|
||||
end
|
||||
|
||||
new_karma_files = get_karma_files(git.added_files.to_a)
|
||||
|
||||
unless new_karma_files.empty?
|
||||
|
||||
if helper.ci?
|
||||
markdown(<<~MARKDOWN)
|
||||
## New karma spec file
|
||||
|
||||
New frontend specs ([except `browser_specs`](https://gitlab.com/gitlab-org/gitlab/blob/3b6fe2f1077eedb0b8aff02a7350234f0b7dc4f9/spec/javascripts/lib/utils/browser_spec.js#L2)) should be
|
||||
[written in jest](https://docs.gitlab.com/ee/development/testing_guide/frontend_testing.html#jest).
|
||||
|
||||
You have created the following tests, please migrate them over to jest:
|
||||
|
||||
* #{new_karma_files.map { |path| "`#{path}`" }.join("\n* ")}
|
||||
MARKDOWN
|
||||
end
|
||||
|
||||
fail "You have created a new karma spec file"
|
||||
|
||||
end
|
||||
|
||||
changed_karma_files = get_karma_files(helper.all_changed_files) - new_karma_files
|
||||
|
||||
return if changed_karma_files.empty?
|
||||
|
||||
warn 'You have edited karma spec files. Please consider migrating them to jest.'
|
||||
|
||||
if helper.ci?
|
||||
markdown(<<~MARKDOWN)
|
||||
## Edited karma files
|
||||
|
||||
You have edited the following karma spec files. Please consider migrating them to jest:
|
||||
|
||||
* #{changed_karma_files.map { |path| "`#{path}`" }.join("\n* ")}
|
||||
|
||||
In order to align with our Iteration value, migration can also be done as a follow-up.
|
||||
|
||||
For more information: [Jestodus epic](https://gitlab.com/groups/gitlab-org/-/epics/895)
|
||||
MARKDOWN
|
||||
end
|
|
@ -155,10 +155,8 @@ graph RL;
|
|||
3_1-1["jest (16 minutes)"];
|
||||
class 3_1-1 criticalPath;
|
||||
click 3_1-1 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=6914204&udv=0"
|
||||
3_1-2["karma (2 minutes)"];
|
||||
click 3_1-3 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=6914200&udv=0"
|
||||
subgraph "Needs `rspec frontend_fixture/rspec-ee frontend_fixture`";
|
||||
3_1-1 & 3_1-2 --> 2_2-2;
|
||||
3_1-1 --> 2_2-2;
|
||||
end
|
||||
|
||||
3_2-1["rspec:coverage (5.3 minutes)"];
|
||||
|
@ -268,10 +266,8 @@ graph RL;
|
|||
3_1-1["jest (16 minutes)"];
|
||||
class 3_1-1 criticalPath;
|
||||
click 3_1-1 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=6914204&udv=0"
|
||||
3_1-2["karma (2 minutes)"];
|
||||
click 3_1-3 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=6914200&udv=0"
|
||||
subgraph "Needs `rspec frontend_fixture/rspec-ee frontend_fixture`";
|
||||
3_1-1 & 3_1-2 --> 2_2-2;
|
||||
3_1-1 --> 2_2-2;
|
||||
end
|
||||
|
||||
3_2-1["rspec:coverage (5.3 minutes)"];
|
||||
|
@ -627,7 +623,6 @@ that is deployed in stage `review`.
|
|||
the `qa` stage's jobs (for example, Review App performance report).
|
||||
- `pages`: This stage includes a job that deploys the various reports as
|
||||
GitLab Pages (for example, [`coverage-ruby`](https://gitlab-org.gitlab.io/gitlab/coverage-ruby/),
|
||||
[`coverage-javascript`](https://gitlab-org.gitlab.io/gitlab/coverage-javascript/),
|
||||
and `webpack-report` (found at `https://gitlab-org.gitlab.io/gitlab/webpack-report/`, but there is
|
||||
[an issue with the deployment](https://gitlab.com/gitlab-org/gitlab/-/issues/233458)).
|
||||
- `notify`: This stage includes jobs that notify various failures to Slack.
|
||||
|
|
|
@ -128,7 +128,6 @@ In order to run the test you can use the following commands:
|
|||
- `bin/rake spec:unit` to run only the unit tests
|
||||
- `bin/rake spec:integration` to run only the integration tests
|
||||
- `bin/rake spec:system` to run only the system tests
|
||||
- `bin/rake karma` to run the Karma test suite
|
||||
|
||||
`bin/rake spec` takes significant time to pass.
|
||||
Instead of running the full test suite locally, you can save a lot of time by running
|
||||
|
|
|
@ -7,7 +7,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
# Frontend testing standards and style guidelines
|
||||
|
||||
There are two types of test suites encountered while developing frontend code
|
||||
at GitLab. We use Karma with Jasmine and Jest for JavaScript unit and integration testing,
|
||||
at GitLab. We use Jest for JavaScript unit and integration testing,
|
||||
and RSpec feature tests with Capybara for e2e (end-to-end) integration testing.
|
||||
|
||||
Unit and feature tests need to be written for all new features.
|
||||
|
@ -28,46 +28,9 @@ If you are looking for a guide on Vue component testing, you can jump right away
|
|||
We use Jest to write frontend unit and integration tests.
|
||||
Jest tests can be found in `/spec/frontend` and `/ee/spec/frontend` in EE.
|
||||
|
||||
## Karma test suite
|
||||
|
||||
While GitLab has switched over to [Jest](https://jestjs.io), Karma tests still exist in our
|
||||
application because some of our specs require a browser and can't be easily migrated to Jest.
|
||||
Those specs intend to eventually drop Karma in favor of either Jest or RSpec. You can track this migration
|
||||
in the [related epic](https://gitlab.com/groups/gitlab-org/-/epics/4900).
|
||||
|
||||
[Karma](http://karma-runner.github.io/) is a test runner which uses
|
||||
[Jasmine](https://jasmine.github.io/) as its test framework. Jest also uses Jasmine as foundation,
|
||||
that's why it's looking quite similar.
|
||||
|
||||
Karma tests live in `spec/javascripts/` and `/ee/spec/javascripts` in EE.
|
||||
|
||||
`app/assets/javascripts/behaviors/autosize.js`
|
||||
might have a corresponding `spec/javascripts/behaviors/autosize_spec.js` file.
|
||||
|
||||
Keep in mind that in a CI environment, these tests are run in a headless
|
||||
browser and you don't have access to certain APIs, such as
|
||||
[`Notification`](https://developer.mozilla.org/en-US/docs/Web/API/notification),
|
||||
which have to be stubbed.
|
||||
|
||||
### Differences to Karma
|
||||
|
||||
- Jest runs in a Node.js environment, not in a browser. [An issue exists](https://gitlab.com/gitlab-org/gitlab/-/issues/26982) for running Jest tests in a browser.
|
||||
- Because Jest runs in a Node.js environment, it uses [jsdom](https://github.com/jsdom/jsdom) by default. See also its [limitations](#limitations-of-jsdom) below.
|
||||
- Jest does not have access to Webpack loaders or aliases.
|
||||
The aliases used by Jest are defined in its [own configuration](https://gitlab.com/gitlab-org/gitlab/-/blob/master/jest.config.js).
|
||||
- All calls to `setTimeout` and `setInterval` are mocked away. See also [Jest Timer Mocks](https://jestjs.io/docs/timer-mocks).
|
||||
- `rewire` is not required because Jest supports mocking modules. See also [Manual Mocks](https://jestjs.io/docs/manual-mocks).
|
||||
- No [context object](https://jasmine.github.io/tutorials/your_first_suite#section-The_%3Ccode%3Ethis%3C/code%3E_keyword) is passed to tests in Jest.
|
||||
This means sharing `this.something` between `beforeEach()` and `it()` for example does not work.
|
||||
Instead you should declare shared variables in the context that they are needed (via `const` / `let`).
|
||||
- The following cause tests to fail in Jest:
|
||||
- Unmocked requests.
|
||||
- Unhandled Promise rejections.
|
||||
- Calls to `console.warn`, including warnings from libraries like Vue.
|
||||
|
||||
### Limitations of jsdom
|
||||
|
||||
As mentioned [above](#differences-to-karma), Jest uses jsdom instead of a browser for running tests.
|
||||
Jest uses jsdom instead of a browser for running tests.
|
||||
This comes with a number of limitations, namely:
|
||||
|
||||
- [No scrolling support](https://github.com/jsdom/jsdom/blob/15.1.1/lib/jsdom/browser/Window.js#L623-L625)
|
||||
|
@ -387,8 +350,7 @@ Sometimes we have to test time-sensitive code. For example, recurring events tha
|
|||
|
||||
If the application itself is waiting for some time, mock await the waiting. In Jest this is already
|
||||
[done by default](https://gitlab.com/gitlab-org/gitlab/-/blob/a2128edfee799e49a8732bfa235e2c5e14949c68/jest.config.js#L47)
|
||||
(see also [Jest Timer Mocks](https://jestjs.io/docs/timer-mocks)). In Karma you can use the
|
||||
[Jasmine mock clock](https://jasmine.github.io/api/2.9/Clock.html).
|
||||
(see also [Jest Timer Mocks](https://jestjs.io/docs/timer-mocks)).
|
||||
|
||||
```javascript
|
||||
const doSomethingLater = () => {
|
||||
|
@ -409,20 +371,6 @@ it('does something', () => {
|
|||
});
|
||||
```
|
||||
|
||||
**in Karma:**
|
||||
|
||||
```javascript
|
||||
it('does something', () => {
|
||||
jasmine.clock().install();
|
||||
|
||||
doSomethingLater();
|
||||
jasmine.clock().tick(4000);
|
||||
|
||||
expect(something).toBe('done');
|
||||
jasmine.clock().uninstall();
|
||||
});
|
||||
```
|
||||
|
||||
### Mocking the current location in Jest
|
||||
|
||||
NOTE:
|
||||
|
@ -476,8 +424,7 @@ it('passes', () => {
|
|||
|
||||
Sometimes a test needs to wait for something to happen in the application before it continues.
|
||||
Avoid using [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout)
|
||||
because it makes the reason for waiting unclear and if used within Karma with a time larger than zero it slows down our test suite.
|
||||
Instead use one of the following approaches.
|
||||
because it makes the reason for waiting unclear. Instead use one of the following approaches.
|
||||
|
||||
#### Promises and Ajax calls
|
||||
|
||||
|
@ -505,19 +452,6 @@ it('waits for an Ajax call', async () => {
|
|||
});
|
||||
```
|
||||
|
||||
**in Karma:**
|
||||
|
||||
```javascript
|
||||
it('waits for an Ajax call', done => {
|
||||
askTheServer()
|
||||
.then(() => {
|
||||
expect(something).toBe('done');
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
```
|
||||
|
||||
If you are not able to register handlers to the `Promise`, for example because it is executed in a synchronous Vue life cycle hook, take a look at the [waitFor](#wait-until-axios-requests-finish) helpers or you can flush all pending `Promise`s:
|
||||
|
||||
**in Jest:**
|
||||
|
@ -548,22 +482,6 @@ it('renders something', () => {
|
|||
});
|
||||
```
|
||||
|
||||
**in Karma:**
|
||||
|
||||
```javascript
|
||||
it('renders something', done => {
|
||||
wrapper.setProps({ value: 'new value' });
|
||||
|
||||
wrapper.vm
|
||||
.$nextTick()
|
||||
.then(() => {
|
||||
expect(wrapper.text()).toBe('new value');
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
```
|
||||
|
||||
#### Events
|
||||
|
||||
If the application triggers an event that you need to wait for in your test, register an event handler which contains
|
||||
|
@ -776,7 +694,7 @@ TBU
|
|||
|
||||
### Stubbing and Mocking
|
||||
|
||||
Jasmine provides stubbing and mocking capabilities. There are some subtle differences in how to use it within Karma and Jest.
|
||||
Jasmine provides stubbing and mocking capabilities. There are some subtle differences in how to use it within Jest.
|
||||
|
||||
Stubs or spies are often used synonymously. In Jest it's quite easy thanks to the `.spyOn` method.
|
||||
[Official docs](https://jestjs.io/docs/jest-object#jestspyonobject-methodname)
|
||||
|
@ -835,7 +753,6 @@ For running the frontend tests, you need the following commands:
|
|||
- `rake frontend:fixtures` (re-)generates [fixtures](#frontend-test-fixtures). Make sure that
|
||||
fixtures are up-to-date before running tests that require them.
|
||||
- `yarn jest` runs Jest tests.
|
||||
- `yarn karma` runs Karma tests.
|
||||
|
||||
### Live testing and focused testing -- Jest
|
||||
|
||||
|
@ -860,50 +777,37 @@ yarn jest ./path/to/folder/
|
|||
yarn jest term
|
||||
```
|
||||
|
||||
### Live testing and focused testing -- Karma
|
||||
|
||||
Karma allows something similar, but it's way more costly.
|
||||
|
||||
Running Karma with `yarn run karma-start` compiles the JavaScript
|
||||
assets and runs a server at `http://localhost:9876/` where it automatically
|
||||
runs the tests on any browser which connects to it. You can enter that URL on
|
||||
multiple browsers at once to have it run the tests on each in parallel.
|
||||
|
||||
While Karma is running, any changes you make instantly trigger a recompile
|
||||
and retest of the **entire test suite**, so you can see instantly if you've broken
|
||||
a test with your changes. You can use [Jasmine focused](https://jasmine.github.io/2.5/focused_specs.html) or
|
||||
excluded tests (with `fdescribe` or `xdescribe`) to get Karma to run only the
|
||||
tests you want while you're working on a specific feature, but make sure to
|
||||
remove these directives when you commit your code.
|
||||
|
||||
It is also possible to only run Karma on specific folders or files by filtering
|
||||
the run tests via the argument `--filter-spec` or short `-f`:
|
||||
|
||||
```shell
|
||||
# Run all files
|
||||
yarn karma-start
|
||||
# Run specific spec files
|
||||
yarn karma-start --filter-spec profile/account/components/update_username_spec.js
|
||||
# Run specific spec folder
|
||||
yarn karma-start --filter-spec profile/account/components/
|
||||
# Run all specs which path contain vue_shared or vie
|
||||
yarn karma-start -f vue_shared -f vue_mr_widget
|
||||
```
|
||||
|
||||
You can also use glob syntax to match files. Remember to put quotes around the
|
||||
glob otherwise your shell may split it into multiple arguments:
|
||||
|
||||
```shell
|
||||
# Run all specs named `file_spec` within the IDE subdirectory
|
||||
yarn karma -f 'spec/javascripts/ide/**/file_spec.js'
|
||||
```
|
||||
|
||||
## Frontend test fixtures
|
||||
|
||||
Frontend fixtures are files containing responses from backend controllers. These responses can be either HTML
|
||||
generated from HAML templates or JSON payloads. Frontend tests that rely on these responses are
|
||||
often using fixtures to validate correct integration with the backend code.
|
||||
|
||||
### Use fixtures
|
||||
|
||||
Jest uses `spec/frontend/__helpers__/fixtures.js` to import fixtures in tests.
|
||||
|
||||
The following are examples of tests that work for Jest:
|
||||
|
||||
```javascript
|
||||
it('makes a request', () => {
|
||||
const responseBody = getJSONFixture('some/fixture.json'); // loads spec/frontend/fixtures/some/fixture.json
|
||||
axiosMock.onGet(endpoint).reply(200, responseBody);
|
||||
|
||||
myButton.click();
|
||||
|
||||
// ...
|
||||
});
|
||||
|
||||
it('uses some HTML element', () => {
|
||||
loadFixtures('some/page.html'); // loads spec/frontend/fixtures/some/page.html and adds it to the DOM
|
||||
|
||||
const element = document.getElementById('#my-id');
|
||||
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
### Generate fixtures
|
||||
|
||||
You can find code to generate test fixtures in:
|
||||
|
@ -961,34 +865,6 @@ This will create a new fixture located at
|
|||
You can import the JSON fixture in a Jest test using the `getJSONFixture` method
|
||||
[as described below](#use-fixtures).
|
||||
|
||||
### Use fixtures
|
||||
|
||||
Jest and Karma test suites import fixtures in different ways:
|
||||
|
||||
- The Karma test suite are served by [jasmine-jquery](https://github.com/velesin/jasmine-jquery).
|
||||
- Jest use `spec/frontend/__helpers__/fixtures.js`.
|
||||
|
||||
The following are examples of tests that work for both Karma and Jest:
|
||||
|
||||
```javascript
|
||||
it('makes a request', () => {
|
||||
const responseBody = getJSONFixture('some/fixture.json'); // loads spec/frontend/fixtures/some/fixture.json
|
||||
axiosMock.onGet(endpoint).reply(200, responseBody);
|
||||
|
||||
myButton.click();
|
||||
|
||||
// ...
|
||||
});
|
||||
|
||||
it('uses some HTML element', () => {
|
||||
loadFixtures('some/page.html'); // loads spec/frontend/fixtures/some/page.html and adds it to the DOM
|
||||
|
||||
const element = document.getElementById('#my-id');
|
||||
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
## Data-driven tests
|
||||
|
||||
Similar to [RSpec's parameterized tests](best_practices.md#table-based--parameterized-tests),
|
||||
|
@ -1139,13 +1015,10 @@ Main information on frontend testing levels can be found in the [Testing Levels
|
|||
|
||||
Tests relevant for frontend development can be found at the following places:
|
||||
|
||||
- `spec/javascripts/`, for Karma tests
|
||||
- `spec/frontend/`, for Jest tests
|
||||
- `spec/features/`, for RSpec tests
|
||||
|
||||
RSpec runs complete [feature tests](testing_levels.md#frontend-feature-tests), while the Jest and Karma directories contain [frontend unit tests](testing_levels.md#frontend-unit-tests), [frontend component tests](testing_levels.md#frontend-component-tests), and [frontend integration tests](testing_levels.md#frontend-integration-tests).
|
||||
|
||||
All tests in `spec/javascripts/` are intended to be migrated to `spec/frontend/` (see also [#52483](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/52483)).
|
||||
RSpec runs complete [feature tests](testing_levels.md#frontend-feature-tests), while the Jest directories contain [frontend unit tests](testing_levels.md#frontend-unit-tests), [frontend component tests](testing_levels.md#frontend-component-tests), and [frontend integration tests](testing_levels.md#frontend-integration-tests).
|
||||
|
||||
Before May 2018, `features/` also contained feature tests run by Spinach. These tests were removed from the codebase in May 2018 ([#23036](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/23036)).
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ importance.
|
|||
|
||||
GitLab is built on top of [Ruby on Rails](https://rubyonrails.org/), and we're using [RSpec](https://github.com/rspec/rspec-rails#feature-specs) for all
|
||||
the backend tests, with [Capybara](https://github.com/teamcapybara/capybara) for end-to-end integration testing.
|
||||
On the frontend side, we're using [Jest](https://jestjs.io/) and [Karma](http://karma-runner.github.io/)/[Jasmine](https://jasmine.github.io/) for JavaScript unit and
|
||||
On the frontend side, we're using [Jest](https://jestjs.io/) for JavaScript unit and
|
||||
integration testing.
|
||||
|
||||
Following are two great articles that everyone should read to understand what
|
||||
|
@ -40,7 +40,7 @@ system tests, parameterized tests etc.
|
|||
|
||||
## [Frontend testing standards and style guidelines](frontend_testing.md)
|
||||
|
||||
Everything you should know about how to write good Frontend tests: Karma,
|
||||
Everything you should know about how to write good Frontend tests: Jest,
|
||||
testing promises, stubbing etc.
|
||||
|
||||
## [Flaky tests](flaky_tests.md)
|
||||
|
|
|
@ -31,7 +31,7 @@ records should use stubs/doubles as much as possible.
|
|||
|
||||
| Code path | Tests path | Testing engine | Notes |
|
||||
| --------- | ---------- | -------------- | ----- |
|
||||
| `app/assets/javascripts/` | `spec/javascripts/`, `spec/frontend/` | Karma & Jest | More details in the [Frontend Testing guide](frontend_testing.md) section. |
|
||||
| `app/assets/javascripts/` | `spec/frontend/` | Jest | More details in the [Frontend Testing guide](frontend_testing.md) section. |
|
||||
| `app/finders/` | `spec/finders/` | RSpec | |
|
||||
| `app/graphql/` | `spec/graphql/` | RSpec | |
|
||||
| `app/helpers/` | `spec/helpers/` | RSpec | |
|
||||
|
@ -233,7 +233,7 @@ They're useful to test permissions, redirections, what view is rendered etc.
|
|||
| `app/controllers/` | `spec/requests/`, `spec/controllers` | RSpec | Request specs are preferred over legacy controller specs. |
|
||||
| `app/mailers/` | `spec/mailers/` | RSpec | |
|
||||
| `lib/api/` | `spec/requests/api/` | RSpec | |
|
||||
| `app/assets/javascripts/` | `spec/javascripts/`, `spec/frontend/` | Karma & Jest | [More details below](#frontend-integration-tests) |
|
||||
| `app/assets/javascripts/` | `spec/frontend/` | Jest | [More details below](#frontend-integration-tests) |
|
||||
|
||||
### Frontend integration tests
|
||||
|
||||
|
@ -322,13 +322,6 @@ controller.instance_variable_set(:@user, user)
|
|||
|
||||
and use methods [deprecated in Rails 5](https://gitlab.com/gitlab-org/gitlab/-/issues/16260).
|
||||
|
||||
### About Karma
|
||||
|
||||
Karma is both in the Unit tests and the Integration tests category. Karma provides an environment to
|
||||
run JavaScript tests, so you can either run unit tests (e.g. test a single
|
||||
JavaScript method), or integration tests (e.g. test a component that is composed
|
||||
of multiple components).
|
||||
|
||||
## White-box tests at the system level (formerly known as System / Feature tests)
|
||||
|
||||
Formal definitions:
|
||||
|
|
|
@ -3,15 +3,3 @@ const baseConfig = require('./jest.config.base');
|
|||
module.exports = {
|
||||
...baseConfig('spec/frontend'),
|
||||
};
|
||||
|
||||
const karmaTestFile = process.argv.find((arg) => arg.includes('spec/javascripts/'));
|
||||
if (karmaTestFile) {
|
||||
console.error(`
|
||||
Files in spec/javascripts/ and ee/spec/javascripts need to be run with Karma.
|
||||
Please use the following command instead:
|
||||
|
||||
yarn karma -f ${karmaTestFile}
|
||||
|
||||
`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
unless Rails.env.production?
|
||||
namespace :karma do
|
||||
# alias exists for legacy reasons
|
||||
desc 'GitLab | Karma | Generate fixtures for JavaScript tests'
|
||||
task fixtures: ['frontend:fixtures']
|
||||
|
||||
desc 'GitLab | Karma | Run JavaScript tests'
|
||||
task tests: ['yarn:check'] do
|
||||
sh "yarn run karma" do |ok, res|
|
||||
abort('rake karma:tests failed') unless ok
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
desc 'GitLab | Karma | Shortcut for karma:fixtures and karma:tests'
|
||||
task karma: ['karma:fixtures', 'karma:tests']
|
||||
end
|
12
package.json
12
package.json
|
@ -15,10 +15,6 @@
|
|||
"jest-debug": "node --inspect-brk node_modules/.bin/jest --runInBand",
|
||||
"jest:integration": "jest --config jest.config.integration.js",
|
||||
"jsdoc": "jsdoc -c config/jsdocs.config.js",
|
||||
"prekarma": "yarn check-dependencies",
|
||||
"karma": "BABEL_ENV=${BABEL_ENV:=karma} karma start --single-run true config/karma.config.js",
|
||||
"karma-coverage": "BABEL_ENV=coverage karma start --single-run true config/karma.config.js",
|
||||
"karma-start": "BABEL_ENV=karma karma start config/karma.config.js",
|
||||
"lint:eslint": "yarn run internal:eslint",
|
||||
"lint:eslint:fix": "yarn run internal:eslint --fix",
|
||||
"lint:eslint:all": "yarn run internal:eslint .",
|
||||
|
@ -242,14 +238,6 @@
|
|||
"jest-util": "^26.5.2",
|
||||
"jsdoc": "^3.5.5",
|
||||
"jsdoc-vue": "^1.0.0",
|
||||
"karma": "^4.2.0",
|
||||
"karma-chrome-launcher": "^3.0.0",
|
||||
"karma-coverage-istanbul-reporter": "^2.1.0",
|
||||
"karma-jasmine": "^1.1.2",
|
||||
"karma-junit-reporter": "^1.2.0",
|
||||
"karma-mocha-reporter": "^2.2.5",
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-webpack": "^4.0.2",
|
||||
"markdownlint-cli": "0.26.0",
|
||||
"md5": "^2.2.1",
|
||||
"miragejs": "^0.1.40",
|
||||
|
|
|
@ -71,8 +71,7 @@ module QA
|
|||
# adequately tested elsewhere.
|
||||
#
|
||||
# There are also FE specs
|
||||
# * ee/spec/javascripts/ide/components/terminal/terminal_spec.js
|
||||
# * ee/spec/frontend/ide/components/terminal/terminal_controls_spec.js
|
||||
# * spec/frontend/ide/components/terminal/terminal_controls_spec.js
|
||||
Page::Project::WebIDE::Edit.perform do |edit|
|
||||
edit.start_web_terminal
|
||||
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
karma_directory=spec/javascripts
|
||||
|
||||
if [ -d ee ]; then
|
||||
karma_directory="$karma_directory ee/$karma_directory"
|
||||
fi
|
||||
|
||||
karma_files=$(find $karma_directory -type f -name '*_spec.js' -not -path '*/helpers/*')
|
||||
violations=""
|
||||
|
||||
for karma_file in $karma_files; do
|
||||
jest_file=${karma_file/spec\/javascripts/"spec/frontend"}
|
||||
|
||||
if [ -f $jest_file ]; then
|
||||
violations="$violations $jest_file"
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -z "$violations" ]]; then
|
||||
echo "All good!"
|
||||
exit 0
|
||||
else
|
||||
echo "Danger! The following Jest specs have corresponding files in the Karma spec directory (i.e. spec/javascripts):"
|
||||
echo ""
|
||||
echo "------------------------------"
|
||||
for file in $violations; do
|
||||
echo $file
|
||||
done
|
||||
echo "------------------------------"
|
||||
echo ""
|
||||
echo "For each of these files, please either:"
|
||||
echo ""
|
||||
echo "1. Fully migrate the file to Jest and remove the corresponding Karma file."
|
||||
echo "2. Remove the Jest file for now, make any relevant changes in the corresponding Karma file, and handle the migration to Jest in a separate MR."
|
||||
echo ""
|
||||
echo "Why is this a problem?"
|
||||
echo ""
|
||||
echo "- It's nice to have a single source of truth for the unit tests of a subject."
|
||||
echo "- This will cause conflicts if the remaining Karma spec is migrated using our automated tool."
|
||||
echo " https://gitlab.com/gitlab-org/frontend/playground/migrate-karma-to-jest"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
|
@ -14,7 +14,7 @@ const fs = require('fs');
|
|||
const path = require('path');
|
||||
|
||||
const sourceDirectories = ['app/assets/javascripts'];
|
||||
const testDirectories = ['spec/javascripts', 'spec/frontend'];
|
||||
const testDirectories = ['spec/frontend'];
|
||||
|
||||
if (fs.existsSync('ee')) {
|
||||
sourceDirectories.forEach((dir) => {
|
||||
|
|
|
@ -51,8 +51,7 @@ class StaticAnalysis
|
|||
Task.new(%w[scripts/lint-conflicts.sh], 1),
|
||||
Task.new(%w[yarn run block-dependencies], 1),
|
||||
Task.new(%w[scripts/lint-rugged], 1),
|
||||
Task.new(%w[scripts/gemfile_lock_changed.sh], 1),
|
||||
Task.new(%w[scripts/frontend/check_no_partial_karma_jest.sh], 1)
|
||||
Task.new(%w[scripts/gemfile_lock_changed.sh], 1)
|
||||
].compact.freeze
|
||||
|
||||
def run_tasks!(options = {})
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
---
|
||||
env:
|
||||
jasmine: true
|
||||
extends: plugin:jasmine/recommended
|
||||
globals:
|
||||
appendLoadFixtures: false
|
||||
appendLoadStyleFixtures: false
|
||||
appendSetFixtures: false
|
||||
appendSetStyleFixtures: false
|
||||
getJSONFixture: false
|
||||
loadFixtures: false
|
||||
loadJSONFixtures: false
|
||||
loadStyleFixtures: false
|
||||
preloadFixtures: false
|
||||
preloadStyleFixtures: false
|
||||
readFixtures: false
|
||||
sandbox: false
|
||||
setFixtures: false
|
||||
setStyleFixtures: false
|
||||
spyOnDependency: false
|
||||
spyOnEvent: false
|
||||
ClassSpecHelper: false
|
||||
plugins:
|
||||
- jasmine
|
||||
rules:
|
||||
func-names: off
|
||||
jasmine/no-suite-dupes:
|
||||
- warn
|
||||
- branch
|
||||
jasmine/no-spec-dupes:
|
||||
- warn
|
||||
- branch
|
||||
prefer-arrow-callback: off
|
||||
import/no-unresolved:
|
||||
- error
|
||||
- ignore:
|
||||
- 'fixtures/blob'
|
||||
# Temporarily disabled to facilitate an upgrade to eslint-plugin-jasmine
|
||||
jasmine/prefer-toHaveBeenCalledWith: off
|
2
spec/javascripts/fixtures/.gitignore
vendored
2
spec/javascripts/fixtures/.gitignore
vendored
|
@ -1,2 +0,0 @@
|
|||
*
|
||||
!.gitignore
|
|
@ -1,334 +0,0 @@
|
|||
// this file can't be migrated to jest because it relies on the browser to perform integration tests:
|
||||
// (specifically getClientBoundingRect and mouse movements)
|
||||
// see: https://gitlab.com/groups/gitlab-org/-/epics/895#what-if-theres-a-karma-spec-which-is-simply-unmovable-to-jest-ie-it-is-dependent-on-a-running-browser-environment
|
||||
|
||||
import { GlBreakpointInstance } from '@gitlab/ui/dist/utils';
|
||||
import { SIDEBAR_COLLAPSED_CLASS } from '~/contextual_sidebar';
|
||||
import {
|
||||
calculateTop,
|
||||
showSubLevelItems,
|
||||
canShowSubItems,
|
||||
canShowActiveSubItems,
|
||||
mouseEnterTopItems,
|
||||
mouseLeaveTopItem,
|
||||
getOpenMenu,
|
||||
setOpenMenu,
|
||||
mousePos,
|
||||
getHideSubItemsInterval,
|
||||
documentMouseMove,
|
||||
getHeaderHeight,
|
||||
setSidebar,
|
||||
subItemsMouseLeave,
|
||||
} from '~/fly_out_nav';
|
||||
|
||||
describe('Fly out sidebar navigation', () => {
|
||||
let el;
|
||||
let breakpointSize = 'lg';
|
||||
|
||||
beforeEach(() => {
|
||||
el = document.createElement('div');
|
||||
el.style.position = 'relative';
|
||||
document.body.appendChild(el);
|
||||
|
||||
spyOn(GlBreakpointInstance, 'getBreakpointSize').and.callFake(() => breakpointSize);
|
||||
|
||||
setOpenMenu(null);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
document.body.innerHTML = '';
|
||||
breakpointSize = 'lg';
|
||||
mousePos.length = 0;
|
||||
|
||||
setSidebar(null);
|
||||
});
|
||||
|
||||
describe('calculateTop', () => {
|
||||
it('returns boundingRect top', () => {
|
||||
const boundingRect = {
|
||||
top: 100,
|
||||
height: 100,
|
||||
};
|
||||
|
||||
expect(calculateTop(boundingRect, 100)).toBe(100);
|
||||
});
|
||||
|
||||
it('returns boundingRect - bottomOverflow', () => {
|
||||
const boundingRect = {
|
||||
top: window.innerHeight - 50,
|
||||
height: 100,
|
||||
};
|
||||
|
||||
expect(calculateTop(boundingRect, 100)).toBe(window.innerHeight - 50);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getHideSubItemsInterval', () => {
|
||||
beforeEach(() => {
|
||||
el.innerHTML =
|
||||
'<div class="sidebar-sub-level-items" style="position: fixed; top: 0; left: 100px; height: 150px;"></div>';
|
||||
});
|
||||
|
||||
it('returns 0 if currentOpenMenu is nil', () => {
|
||||
expect(getHideSubItemsInterval()).toBe(0);
|
||||
});
|
||||
|
||||
it('returns 0 if mousePos is empty', () => {
|
||||
expect(getHideSubItemsInterval()).toBe(0);
|
||||
});
|
||||
|
||||
it('returns 0 when mouse above sub-items', () => {
|
||||
showSubLevelItems(el);
|
||||
documentMouseMove({
|
||||
clientX: el.getBoundingClientRect().left,
|
||||
clientY: el.getBoundingClientRect().top,
|
||||
});
|
||||
documentMouseMove({
|
||||
clientX: el.getBoundingClientRect().left,
|
||||
clientY: el.getBoundingClientRect().top - 50,
|
||||
});
|
||||
|
||||
expect(getHideSubItemsInterval()).toBe(0);
|
||||
});
|
||||
|
||||
it('returns 0 when mouse is below sub-items', () => {
|
||||
const subItems = el.querySelector('.sidebar-sub-level-items');
|
||||
|
||||
showSubLevelItems(el);
|
||||
documentMouseMove({
|
||||
clientX: el.getBoundingClientRect().left,
|
||||
clientY: el.getBoundingClientRect().top,
|
||||
});
|
||||
documentMouseMove({
|
||||
clientX: el.getBoundingClientRect().left,
|
||||
clientY: el.getBoundingClientRect().top - subItems.getBoundingClientRect().height + 50,
|
||||
});
|
||||
|
||||
expect(getHideSubItemsInterval()).toBe(0);
|
||||
});
|
||||
|
||||
it('returns 300 when mouse is moved towards sub-items', () => {
|
||||
documentMouseMove({
|
||||
clientX: el.getBoundingClientRect().left,
|
||||
clientY: el.getBoundingClientRect().top,
|
||||
});
|
||||
showSubLevelItems(el);
|
||||
documentMouseMove({
|
||||
clientX: el.getBoundingClientRect().left + 20,
|
||||
clientY: el.getBoundingClientRect().top + 10,
|
||||
});
|
||||
|
||||
expect(getHideSubItemsInterval()).toBe(300);
|
||||
});
|
||||
});
|
||||
|
||||
describe('mouseLeaveTopItem', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(el.classList, 'remove');
|
||||
});
|
||||
|
||||
it('removes is-over class if currentOpenMenu is null', () => {
|
||||
mouseLeaveTopItem(el);
|
||||
|
||||
expect(el.classList.remove).toHaveBeenCalledWith('is-over');
|
||||
});
|
||||
|
||||
it('removes is-over class if currentOpenMenu is null & there are sub-items', () => {
|
||||
el.innerHTML = '<div class="sidebar-sub-level-items" style="position: absolute;"></div>';
|
||||
|
||||
mouseLeaveTopItem(el);
|
||||
|
||||
expect(el.classList.remove).toHaveBeenCalledWith('is-over');
|
||||
});
|
||||
|
||||
it('does not remove is-over class if currentOpenMenu is the passed in sub-items', () => {
|
||||
el.innerHTML = '<div class="sidebar-sub-level-items" style="position: absolute;"></div>';
|
||||
|
||||
setOpenMenu(el.querySelector('.sidebar-sub-level-items'));
|
||||
mouseLeaveTopItem(el);
|
||||
|
||||
expect(el.classList.remove).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('mouseEnterTopItems', () => {
|
||||
beforeEach(() => {
|
||||
el.innerHTML =
|
||||
'<div class="sidebar-sub-level-items" style="position: absolute; top: 0; left: 100px; height: 200px;"></div>';
|
||||
});
|
||||
|
||||
it('shows sub-items after 0ms if no menu is open', (done) => {
|
||||
mouseEnterTopItems(el);
|
||||
|
||||
expect(getHideSubItemsInterval()).toBe(0);
|
||||
|
||||
setTimeout(() => {
|
||||
expect(el.querySelector('.sidebar-sub-level-items').style.display).toBe('block');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('shows sub-items after 300ms if a menu is currently open', (done) => {
|
||||
documentMouseMove({
|
||||
clientX: el.getBoundingClientRect().left,
|
||||
clientY: el.getBoundingClientRect().top,
|
||||
});
|
||||
|
||||
setOpenMenu(el.querySelector('.sidebar-sub-level-items'));
|
||||
|
||||
documentMouseMove({
|
||||
clientX: el.getBoundingClientRect().left + 20,
|
||||
clientY: el.getBoundingClientRect().top + 10,
|
||||
});
|
||||
|
||||
mouseEnterTopItems(el, 0);
|
||||
|
||||
expect(getHideSubItemsInterval()).toBe(300);
|
||||
|
||||
setTimeout(() => {
|
||||
expect(el.querySelector('.sidebar-sub-level-items').style.display).toBe('block');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('showSubLevelItems', () => {
|
||||
beforeEach(() => {
|
||||
el.innerHTML = '<div class="sidebar-sub-level-items" style="position: absolute;"></div>';
|
||||
});
|
||||
|
||||
it('adds is-over class to el', () => {
|
||||
spyOn(el.classList, 'add');
|
||||
|
||||
showSubLevelItems(el);
|
||||
|
||||
expect(el.classList.add).toHaveBeenCalledWith('is-over');
|
||||
});
|
||||
|
||||
it('does not show sub-items on mobile', () => {
|
||||
breakpointSize = 'xs';
|
||||
|
||||
showSubLevelItems(el);
|
||||
|
||||
expect(el.querySelector('.sidebar-sub-level-items').style.display).not.toBe('block');
|
||||
});
|
||||
|
||||
it('shows sub-items', () => {
|
||||
showSubLevelItems(el);
|
||||
|
||||
expect(el.querySelector('.sidebar-sub-level-items').style.display).toBe('block');
|
||||
});
|
||||
|
||||
it('shows collapsed only sub-items if icon only sidebar', () => {
|
||||
const subItems = el.querySelector('.sidebar-sub-level-items');
|
||||
const sidebar = document.createElement('div');
|
||||
sidebar.classList.add(SIDEBAR_COLLAPSED_CLASS);
|
||||
subItems.classList.add('is-fly-out-only');
|
||||
|
||||
setSidebar(sidebar);
|
||||
|
||||
showSubLevelItems(el);
|
||||
|
||||
expect(el.querySelector('.sidebar-sub-level-items').style.display).toBe('block');
|
||||
});
|
||||
|
||||
it('does not show collapsed only sub-items if icon only sidebar', () => {
|
||||
const subItems = el.querySelector('.sidebar-sub-level-items');
|
||||
subItems.classList.add('is-fly-out-only');
|
||||
|
||||
showSubLevelItems(el);
|
||||
|
||||
expect(subItems.style.display).not.toBe('block');
|
||||
});
|
||||
|
||||
it('sets transform of sub-items', () => {
|
||||
const sidebar = document.createElement('div');
|
||||
const subItems = el.querySelector('.sidebar-sub-level-items');
|
||||
|
||||
sidebar.style.width = '200px';
|
||||
|
||||
document.body.appendChild(sidebar);
|
||||
|
||||
setSidebar(sidebar);
|
||||
showSubLevelItems(el);
|
||||
|
||||
expect(subItems.style.transform).toBe(
|
||||
`translate3d(200px, ${
|
||||
Math.floor(el.getBoundingClientRect().top) - getHeaderHeight()
|
||||
}px, 0px)`,
|
||||
);
|
||||
});
|
||||
|
||||
it('sets is-above when element is above', () => {
|
||||
const subItems = el.querySelector('.sidebar-sub-level-items');
|
||||
subItems.style.height = `${window.innerHeight + el.offsetHeight}px`;
|
||||
el.style.top = `${window.innerHeight - el.offsetHeight}px`;
|
||||
|
||||
spyOn(subItems.classList, 'add');
|
||||
|
||||
showSubLevelItems(el);
|
||||
|
||||
expect(subItems.classList.add).toHaveBeenCalledWith('is-above');
|
||||
});
|
||||
});
|
||||
|
||||
describe('canShowSubItems', () => {
|
||||
it('returns true if on desktop size', () => {
|
||||
expect(canShowSubItems()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns false if on mobile size', () => {
|
||||
breakpointSize = 'xs';
|
||||
|
||||
expect(canShowSubItems()).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('canShowActiveSubItems', () => {
|
||||
it('returns true by default', () => {
|
||||
expect(canShowActiveSubItems(el)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns false when active & expanded sidebar', () => {
|
||||
const sidebar = document.createElement('div');
|
||||
el.classList.add('active');
|
||||
|
||||
setSidebar(sidebar);
|
||||
|
||||
expect(canShowActiveSubItems(el)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('returns true when active & collapsed sidebar', () => {
|
||||
const sidebar = document.createElement('div');
|
||||
sidebar.classList.add(SIDEBAR_COLLAPSED_CLASS);
|
||||
el.classList.add('active');
|
||||
|
||||
setSidebar(sidebar);
|
||||
|
||||
expect(canShowActiveSubItems(el)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('subItemsMouseLeave', () => {
|
||||
beforeEach(() => {
|
||||
el.innerHTML = '<div class="sidebar-sub-level-items" style="position: absolute;"></div>';
|
||||
|
||||
setOpenMenu(el.querySelector('.sidebar-sub-level-items'));
|
||||
});
|
||||
|
||||
it('hides subMenu if element is not hovered', () => {
|
||||
subItemsMouseLeave(el);
|
||||
|
||||
expect(getOpenMenu()).toBeNull();
|
||||
});
|
||||
|
||||
it('does not hide subMenu if element is hovered', () => {
|
||||
el.classList.add('is-over');
|
||||
subItemsMouseLeave(el);
|
||||
|
||||
expect(getOpenMenu()).not.toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1 +0,0 @@
|
|||
export * from '../../../frontend/lib/utils/mock_data';
|
|
@ -1,145 +0,0 @@
|
|||
/* eslint-disable
|
||||
jasmine/no-global-setup, no-underscore-dangle, no-console
|
||||
*/
|
||||
|
||||
import { config as testUtilsConfig } from '@vue/test-utils';
|
||||
import jasmineDiff from 'jasmine-diff';
|
||||
import $ from 'jquery';
|
||||
import 'core-js/features/set-immediate';
|
||||
import 'vendor/jasmine-jquery';
|
||||
import '~/commons';
|
||||
import Vue from 'vue';
|
||||
import { getDefaultAdapter } from '~/lib/utils/axios_utils';
|
||||
import Translate from '~/vue_shared/translate';
|
||||
|
||||
import { FIXTURES_PATH, TEST_HOST } from './test_constants';
|
||||
|
||||
// Tech debt issue TBD
|
||||
testUtilsConfig.logModifiedComponents = false;
|
||||
|
||||
const isHeadlessChrome = /\bHeadlessChrome\//.test(navigator.userAgent);
|
||||
Vue.config.devtools = !isHeadlessChrome;
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
let hasVueWarnings = false;
|
||||
Vue.config.warnHandler = (msg, vm, trace) => {
|
||||
// The following workaround is necessary, so we are able to use setProps from Vue test utils
|
||||
// see https://github.com/vuejs/vue-test-utils/issues/631#issuecomment-421108344
|
||||
const currentStack = new Error().stack;
|
||||
const isInVueTestUtils = currentStack
|
||||
.split('\n')
|
||||
.some((line) => line.startsWith(' at VueWrapper.setProps ('));
|
||||
if (isInVueTestUtils) {
|
||||
return;
|
||||
}
|
||||
|
||||
hasVueWarnings = true;
|
||||
fail(`${msg}${trace}`);
|
||||
};
|
||||
|
||||
let hasVueErrors = false;
|
||||
Vue.config.errorHandler = function (err) {
|
||||
hasVueErrors = true;
|
||||
fail(err);
|
||||
};
|
||||
|
||||
Vue.use(Translate);
|
||||
|
||||
// enable test fixtures
|
||||
jasmine.getFixtures().fixturesPath = FIXTURES_PATH;
|
||||
jasmine.getJSONFixtures().fixturesPath = FIXTURES_PATH;
|
||||
|
||||
beforeAll(() => {
|
||||
jasmine.addMatchers(
|
||||
jasmineDiff(jasmine, {
|
||||
colors: window.__karma__.config.color,
|
||||
inline: window.__karma__.config.color,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
// globalize common libraries
|
||||
window.$ = $;
|
||||
window.jQuery = window.$;
|
||||
|
||||
// stub expected globals
|
||||
window.gl = window.gl || {};
|
||||
window.gl.TEST_HOST = TEST_HOST;
|
||||
window.gon = window.gon || {};
|
||||
window.gon.test_env = true;
|
||||
window.gon.ee = process.env.IS_EE;
|
||||
gon.relative_url_root = '';
|
||||
|
||||
let hasUnhandledPromiseRejections = false;
|
||||
|
||||
window.addEventListener('unhandledrejection', (event) => {
|
||||
hasUnhandledPromiseRejections = true;
|
||||
console.error('Unhandled promise rejection:');
|
||||
console.error(event.reason.stack || event.reason);
|
||||
});
|
||||
|
||||
let longRunningTestTimeoutHandle;
|
||||
|
||||
beforeEach((done) => {
|
||||
longRunningTestTimeoutHandle = setTimeout(() => {
|
||||
done.fail('Test is running too long!');
|
||||
}, 4000);
|
||||
done();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
clearTimeout(longRunningTestTimeoutHandle);
|
||||
});
|
||||
|
||||
const axiosDefaultAdapter = getDefaultAdapter();
|
||||
|
||||
// render all of our tests
|
||||
const testContexts = [require.context('spec', true, /_spec$/)];
|
||||
|
||||
if (process.env.IS_EE) {
|
||||
testContexts.push(require.context('ee_spec', true, /_spec$/));
|
||||
}
|
||||
|
||||
testContexts.forEach((context) => {
|
||||
context.keys().forEach((path) => {
|
||||
try {
|
||||
context(path);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
console.error('[GL SPEC RUNNER ERROR] Unable to load spec: ', path);
|
||||
describe('Test bundle', function () {
|
||||
it(`includes '${path}'`, function () {
|
||||
expect(err).toBeNull();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('test errors', () => {
|
||||
beforeAll((done) => {
|
||||
if (hasUnhandledPromiseRejections || hasVueWarnings || hasVueErrors) {
|
||||
setTimeout(done, 1000);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
it('has no unhandled Promise rejections', () => {
|
||||
expect(hasUnhandledPromiseRejections).toBe(false);
|
||||
});
|
||||
|
||||
it('has no Vue warnings', () => {
|
||||
expect(hasVueWarnings).toBe(false);
|
||||
});
|
||||
|
||||
it('has no Vue error', () => {
|
||||
expect(hasVueErrors).toBe(false);
|
||||
});
|
||||
|
||||
it('restores axios adapter after mocking', () => {
|
||||
if (getDefaultAdapter() !== axiosDefaultAdapter) {
|
||||
fail('axios adapter is not restored! Did you forget a restore() on MockAdapter?');
|
||||
}
|
||||
});
|
||||
});
|
|
@ -1 +0,0 @@
|
|||
export * from '../frontend/__helpers__/test_constants';
|
|
@ -60,7 +60,6 @@
|
|||
'app/views/foo' | [:frontend]
|
||||
'public/foo' | [:frontend]
|
||||
'scripts/frontend/foo' | [:frontend]
|
||||
'spec/javascripts/foo' | [:frontend]
|
||||
'spec/frontend/bar' | [:frontend]
|
||||
'spec/frontend_integration/bar' | [:frontend]
|
||||
'vendor/assets/foo' | [:frontend]
|
||||
|
@ -73,7 +72,6 @@
|
|||
|
||||
'ee/app/assets/foo' | [:frontend]
|
||||
'ee/app/views/foo' | [:frontend]
|
||||
'ee/spec/javascripts/foo' | [:frontend]
|
||||
'ee/spec/frontend/bar' | [:frontend]
|
||||
'ee/spec/frontend_integration/bar' | [:frontend]
|
||||
|
||||
|
@ -220,7 +218,7 @@
|
|||
|
||||
describe '.local_warning_message' do
|
||||
it 'returns an informational message with rules that can run' do
|
||||
expect(described_class.local_warning_message).to eq('==> Only the following Danger rules can be run locally: changelog, database, documentation, duplicate_yarn_dependencies, eslint, gitaly, karma, pajamas, pipeline, prettier, product_intelligence, utility_css, vue_shared_documentation')
|
||||
expect(described_class.local_warning_message).to eq('==> Only the following Danger rules can be run locally: changelog, database, documentation, duplicate_yarn_dependencies, eslint, gitaly, pajamas, pipeline, prettier, product_intelligence, utility_css, vue_shared_documentation')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ module ProjectHelper
|
|||
duplicate_yarn_dependencies
|
||||
eslint
|
||||
gitaly
|
||||
karma
|
||||
pajamas
|
||||
pipeline
|
||||
prettier
|
||||
|
|
854
vendor/assets/javascripts/jasmine-jquery.js
vendored
854
vendor/assets/javascripts/jasmine-jquery.js
vendored
|
@ -1,854 +0,0 @@
|
|||
/* eslint-disable */
|
||||
/*
|
||||
Jasmine JQuery 3.0 patched version from this fork : https://github.com/cmrd-senya/jasmine-jquery/blob/jquery3/lib/jasmine-jquery.js
|
||||
*/
|
||||
/*!
|
||||
Jasmine-jQuery: a set of jQuery helpers for Jasmine tests.
|
||||
|
||||
Version 2.1.1
|
||||
|
||||
https://github.com/velesin/jasmine-jquery
|
||||
|
||||
Copyright (c) 2010-2014 Wojciech Zawistowski, Travis Jeffery
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
(function (root, factory) {
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
// The line below is patched from jquery => jquery/dist/jquery
|
||||
// in order to load a jQuery with ajax, so that this testing library
|
||||
// doesn't break
|
||||
factory(root, root.jasmine, require('jquery/dist/jquery'));
|
||||
} else {
|
||||
factory(root, root.jasmine, root.jQuery);
|
||||
}
|
||||
}((function() {return this; })(), function (window, jasmine, $) { "use strict";
|
||||
|
||||
jasmine.spiedEventsKey = function (selector, eventName) {
|
||||
for(var i = 0;; i++) {
|
||||
var key = [eventName, i].toString()
|
||||
if (data.spiedEvents[key] === undefined || data.spiedEvents[key].selector.is($(selector))) {
|
||||
return key
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jasmine.getFixtures = function () {
|
||||
return jasmine.currentFixtures_ = jasmine.currentFixtures_ || new jasmine.Fixtures()
|
||||
}
|
||||
|
||||
jasmine.getStyleFixtures = function () {
|
||||
return jasmine.currentStyleFixtures_ = jasmine.currentStyleFixtures_ || new jasmine.StyleFixtures()
|
||||
}
|
||||
|
||||
jasmine.Fixtures = function () {
|
||||
this.containerId = 'jasmine-fixtures'
|
||||
this.fixturesCache_ = {}
|
||||
this.fixturesPath = 'spec/javascripts/fixtures'
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.set = function (html) {
|
||||
this.cleanUp()
|
||||
return this.createContainer_(html)
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.appendSet= function (html) {
|
||||
this.addToContainer_(html)
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.preload = function () {
|
||||
this.read.apply(this, arguments)
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.load = function () {
|
||||
this.cleanUp()
|
||||
this.createContainer_(this.read.apply(this, arguments))
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.appendLoad = function () {
|
||||
this.addToContainer_(this.read.apply(this, arguments))
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.read = function () {
|
||||
var htmlChunks = []
|
||||
, fixtureUrls = arguments
|
||||
|
||||
for(var urlCount = fixtureUrls.length, urlIndex = 0; urlIndex < urlCount; urlIndex++) {
|
||||
htmlChunks.push(this.getFixtureHtml_(fixtureUrls[urlIndex]))
|
||||
}
|
||||
|
||||
return htmlChunks.join('')
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.clearCache = function () {
|
||||
this.fixturesCache_ = {}
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.cleanUp = function () {
|
||||
$('#' + this.containerId).remove()
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.sandbox = function (attributes) {
|
||||
var attributesToSet = attributes || {}
|
||||
return $('<div id="sandbox" />').attr(attributesToSet)
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.createContainer_ = function (html) {
|
||||
var container = $('<div>')
|
||||
.attr('id', this.containerId)
|
||||
.html(html)
|
||||
|
||||
$(document.body).append(container)
|
||||
return container
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.addToContainer_ = function (html){
|
||||
var container = $(document.body).find('#'+this.containerId).append(html)
|
||||
|
||||
if (!container.length) {
|
||||
this.createContainer_(html)
|
||||
}
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.getFixtureHtml_ = function (url) {
|
||||
if (typeof this.fixturesCache_[url] === 'undefined') {
|
||||
this.loadFixtureIntoCache_(url)
|
||||
}
|
||||
return this.fixturesCache_[url]
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.loadFixtureIntoCache_ = function (relativeUrl) {
|
||||
var self = this
|
||||
, url = this.makeFixtureUrl_(relativeUrl)
|
||||
, htmlText = ''
|
||||
, request = $.ajax({
|
||||
async: false, // must be synchronous to guarantee that no tests are run before fixture is loaded
|
||||
cache: false,
|
||||
url: url,
|
||||
dataType: 'html',
|
||||
success: function (data, status, $xhr) {
|
||||
htmlText = $xhr.responseText
|
||||
}
|
||||
}).fail(function ($xhr, status, err) {
|
||||
throw new Error('Fixture could not be loaded: ' + url + ' (status: ' + status + ', message: ' + err.message + ')')
|
||||
})
|
||||
|
||||
var scripts = $($.parseHTML(htmlText, true)).find('script[src]') || [];
|
||||
|
||||
scripts.each(function(){
|
||||
$.ajax({
|
||||
async: false, // must be synchronous to guarantee that no tests are run before fixture is loaded
|
||||
cache: false,
|
||||
dataType: 'script',
|
||||
url: $(this).attr('src'),
|
||||
success: function (data, status, $xhr) {
|
||||
htmlText += '<script>' + $xhr.responseText + '</script>'
|
||||
},
|
||||
error: function ($xhr, status, err) {
|
||||
throw new Error('Script could not be loaded: ' + url + ' (status: ' + status + ', message: ' + err.message + ')')
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
self.fixturesCache_[relativeUrl] = htmlText;
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.makeFixtureUrl_ = function (relativeUrl){
|
||||
return this.fixturesPath.match('/$') ? this.fixturesPath + relativeUrl : this.fixturesPath + '/' + relativeUrl
|
||||
}
|
||||
|
||||
jasmine.Fixtures.prototype.proxyCallTo_ = function (methodName, passedArguments) {
|
||||
return this[methodName].apply(this, passedArguments)
|
||||
}
|
||||
|
||||
|
||||
jasmine.StyleFixtures = function () {
|
||||
this.fixturesCache_ = {}
|
||||
this.fixturesNodes_ = []
|
||||
this.fixturesPath = 'spec/javascripts/fixtures'
|
||||
}
|
||||
|
||||
jasmine.StyleFixtures.prototype.set = function (css) {
|
||||
this.cleanUp()
|
||||
this.createStyle_(css)
|
||||
}
|
||||
|
||||
jasmine.StyleFixtures.prototype.appendSet = function (css) {
|
||||
this.createStyle_(css)
|
||||
}
|
||||
|
||||
jasmine.StyleFixtures.prototype.preload = function () {
|
||||
this.read_.apply(this, arguments)
|
||||
}
|
||||
|
||||
jasmine.StyleFixtures.prototype.load = function () {
|
||||
this.cleanUp()
|
||||
this.createStyle_(this.read_.apply(this, arguments))
|
||||
}
|
||||
|
||||
jasmine.StyleFixtures.prototype.appendLoad = function () {
|
||||
this.createStyle_(this.read_.apply(this, arguments))
|
||||
}
|
||||
|
||||
jasmine.StyleFixtures.prototype.cleanUp = function () {
|
||||
while(this.fixturesNodes_.length) {
|
||||
this.fixturesNodes_.pop().remove()
|
||||
}
|
||||
}
|
||||
|
||||
jasmine.StyleFixtures.prototype.createStyle_ = function (html) {
|
||||
var styleText = $('<div></div>').html(html).text()
|
||||
, style = $('<style>' + styleText + '</style>')
|
||||
|
||||
this.fixturesNodes_.push(style)
|
||||
$('head').append(style)
|
||||
}
|
||||
|
||||
jasmine.StyleFixtures.prototype.clearCache = jasmine.Fixtures.prototype.clearCache
|
||||
jasmine.StyleFixtures.prototype.read_ = jasmine.Fixtures.prototype.read
|
||||
jasmine.StyleFixtures.prototype.getFixtureHtml_ = jasmine.Fixtures.prototype.getFixtureHtml_
|
||||
jasmine.StyleFixtures.prototype.loadFixtureIntoCache_ = jasmine.Fixtures.prototype.loadFixtureIntoCache_
|
||||
jasmine.StyleFixtures.prototype.makeFixtureUrl_ = jasmine.Fixtures.prototype.makeFixtureUrl_
|
||||
jasmine.StyleFixtures.prototype.proxyCallTo_ = jasmine.Fixtures.prototype.proxyCallTo_
|
||||
|
||||
jasmine.getJSONFixtures = function () {
|
||||
return jasmine.currentJSONFixtures_ = jasmine.currentJSONFixtures_ || new jasmine.JSONFixtures()
|
||||
}
|
||||
|
||||
jasmine.JSONFixtures = function () {
|
||||
this.fixturesCache_ = {}
|
||||
this.fixturesPath = 'spec/javascripts/fixtures/json'
|
||||
}
|
||||
|
||||
jasmine.JSONFixtures.prototype.load = function () {
|
||||
this.read.apply(this, arguments)
|
||||
return this.fixturesCache_
|
||||
}
|
||||
|
||||
jasmine.JSONFixtures.prototype.read = function () {
|
||||
var fixtureUrls = arguments
|
||||
|
||||
for(var urlCount = fixtureUrls.length, urlIndex = 0; urlIndex < urlCount; urlIndex++) {
|
||||
this.getFixtureData_(fixtureUrls[urlIndex])
|
||||
}
|
||||
|
||||
return this.fixturesCache_
|
||||
}
|
||||
|
||||
jasmine.JSONFixtures.prototype.clearCache = function () {
|
||||
this.fixturesCache_ = {}
|
||||
}
|
||||
|
||||
jasmine.JSONFixtures.prototype.getFixtureData_ = function (url) {
|
||||
if (!this.fixturesCache_[url]) this.loadFixtureIntoCache_(url)
|
||||
return this.fixturesCache_[url]
|
||||
}
|
||||
|
||||
jasmine.JSONFixtures.prototype.loadFixtureIntoCache_ = function (relativeUrl) {
|
||||
var self = this
|
||||
, url = this.fixturesPath.match('/$') ? this.fixturesPath + relativeUrl : this.fixturesPath + '/' + relativeUrl
|
||||
|
||||
$.ajax({
|
||||
async: false, // must be synchronous to guarantee that no tests are run before fixture is loaded
|
||||
cache: false,
|
||||
dataType: 'json',
|
||||
url: url,
|
||||
success: function (data) {
|
||||
self.fixturesCache_[relativeUrl] = data
|
||||
},
|
||||
error: function ($xhr, status, err) {
|
||||
throw new Error('JSONFixture could not be loaded: ' + url + ' (status: ' + status + ', message: ' + err.message + ')')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
jasmine.JSONFixtures.prototype.proxyCallTo_ = function (methodName, passedArguments) {
|
||||
return this[methodName].apply(this, passedArguments)
|
||||
}
|
||||
|
||||
jasmine.jQuery = function () {}
|
||||
|
||||
jasmine.jQuery.browserTagCaseIndependentHtml = function (html) {
|
||||
return $('<div/>').append(html).html()
|
||||
}
|
||||
|
||||
jasmine.jQuery.elementToString = function (element) {
|
||||
return $(element).map(function () { return this.outerHTML; }).toArray().join(', ')
|
||||
}
|
||||
|
||||
var data = {
|
||||
spiedEvents: {}
|
||||
, handlers: []
|
||||
}
|
||||
|
||||
jasmine.jQuery.events = {
|
||||
spyOn: function (selector, eventName) {
|
||||
var handler = function (e) {
|
||||
var calls = (typeof data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] !== 'undefined') ? data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)].calls : 0
|
||||
data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] = {
|
||||
selector: $(selector),
|
||||
args: jasmine.util.argsToArray(arguments),
|
||||
calls: ++calls
|
||||
}
|
||||
}
|
||||
|
||||
$(selector).on(eventName, handler)
|
||||
data.handlers.push(handler)
|
||||
|
||||
return {
|
||||
selector: selector,
|
||||
eventName: eventName,
|
||||
handler: handler,
|
||||
reset: function (){
|
||||
delete data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)]
|
||||
},
|
||||
calls: {
|
||||
count: function () {
|
||||
return data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] ?
|
||||
data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)].calls : 0;
|
||||
},
|
||||
any: function () {
|
||||
return data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] ?
|
||||
!!data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)].calls : false;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
args: function (selector, eventName) {
|
||||
var actualArgs = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)].args
|
||||
|
||||
if (!actualArgs) {
|
||||
throw "There is no spy for " + eventName + " on " + selector.toString() + ". Make sure to create a spy using spyOnEvent."
|
||||
}
|
||||
|
||||
return actualArgs
|
||||
},
|
||||
|
||||
wasTriggered: function (selector, eventName) {
|
||||
return !!(data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)])
|
||||
},
|
||||
|
||||
wasTriggeredWith: function (selector, eventName, expectedArgs, util, customEqualityTesters) {
|
||||
var actualArgs = jasmine.jQuery.events.args(selector, eventName).slice(1)
|
||||
|
||||
if (Object.prototype.toString.call(expectedArgs) !== '[object Array]')
|
||||
actualArgs = actualArgs[0]
|
||||
|
||||
return util.equals(actualArgs, expectedArgs, customEqualityTesters)
|
||||
},
|
||||
|
||||
wasPrevented: function (selector, eventName) {
|
||||
var spiedEvent = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)]
|
||||
, args = (jasmine.util.isUndefined(spiedEvent)) ? {} : spiedEvent.args
|
||||
, e = args ? args[0] : undefined
|
||||
|
||||
return e && e.isDefaultPrevented()
|
||||
},
|
||||
|
||||
wasStopped: function (selector, eventName) {
|
||||
var spiedEvent = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)]
|
||||
, args = (jasmine.util.isUndefined(spiedEvent)) ? {} : spiedEvent.args
|
||||
, e = args ? args[0] : undefined
|
||||
|
||||
return e && e.isPropagationStopped()
|
||||
},
|
||||
|
||||
cleanUp: function () {
|
||||
data.spiedEvents = {}
|
||||
data.handlers = []
|
||||
}
|
||||
}
|
||||
|
||||
var hasProperty = function (actualValue, expectedValue) {
|
||||
if (expectedValue === undefined)
|
||||
return actualValue !== undefined
|
||||
|
||||
return actualValue === expectedValue
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
jasmine.addMatchers({
|
||||
toHaveClass: function () {
|
||||
return {
|
||||
compare: function (actual, className) {
|
||||
return { pass: $(actual).hasClass(className) }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toHaveCss: function () {
|
||||
return {
|
||||
compare: function (actual, css) {
|
||||
var stripCharsRegex = /[\s;\"\']/g
|
||||
for (var prop in css) {
|
||||
var value = css[prop]
|
||||
// see issue #147 on gh
|
||||
;if ((value === 'auto') && ($(actual).get(0).style[prop] === 'auto')) continue
|
||||
var actualStripped = $(actual).css(prop).replace(stripCharsRegex, '')
|
||||
var valueStripped = value.replace(stripCharsRegex, '')
|
||||
if (actualStripped !== valueStripped) return { pass: false }
|
||||
}
|
||||
return { pass: true }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toBeVisible: function () {
|
||||
return {
|
||||
compare: function (actual) {
|
||||
return { pass: $(actual).is(':visible') }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toBeHidden: function () {
|
||||
return {
|
||||
compare: function (actual) {
|
||||
return { pass: $(actual).is(':hidden') }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toBeSelected: function () {
|
||||
return {
|
||||
compare: function (actual) {
|
||||
return { pass: $(actual).is(':selected') }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toBeChecked: function () {
|
||||
return {
|
||||
compare: function (actual) {
|
||||
return { pass: $(actual).is(':checked') }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toBeEmpty: function () {
|
||||
return {
|
||||
compare: function (actual) {
|
||||
return { pass: $(actual).is(':empty') }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toBeInDOM: function () {
|
||||
return {
|
||||
compare: function (actual) {
|
||||
return { pass: $.contains(document.documentElement, $(actual)[0]) }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toExist: function () {
|
||||
return {
|
||||
compare: function (actual) {
|
||||
return { pass: $(actual).length }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toHaveLength: function () {
|
||||
return {
|
||||
compare: function (actual, length) {
|
||||
return { pass: $(actual).length === length }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toHaveAttr: function () {
|
||||
return {
|
||||
compare: function (actual, attributeName, expectedAttributeValue) {
|
||||
return { pass: hasProperty($(actual).attr(attributeName), expectedAttributeValue) }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toHaveProp: function () {
|
||||
return {
|
||||
compare: function (actual, propertyName, expectedPropertyValue) {
|
||||
return { pass: hasProperty($(actual).prop(propertyName), expectedPropertyValue) }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toHaveId: function () {
|
||||
return {
|
||||
compare: function (actual, id) {
|
||||
return { pass: $(actual).attr('id') == id }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toHaveHtml: function () {
|
||||
return {
|
||||
compare: function (actual, html) {
|
||||
return { pass: $(actual).html() == jasmine.jQuery.browserTagCaseIndependentHtml(html) }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toContainHtml: function () {
|
||||
return {
|
||||
compare: function (actual, html) {
|
||||
var actualHtml = $(actual).html()
|
||||
, expectedHtml = jasmine.jQuery.browserTagCaseIndependentHtml(html)
|
||||
|
||||
return { pass: (actualHtml.indexOf(expectedHtml) >= 0) }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toHaveText: function () {
|
||||
return {
|
||||
compare: function (actual, text) {
|
||||
var actualText = $(actual).text()
|
||||
var trimmedText = $.trim(actualText)
|
||||
|
||||
if (text && $.isFunction(text.test)) {
|
||||
return { pass: text.test(actualText) || text.test(trimmedText) }
|
||||
} else {
|
||||
return { pass: (actualText == text || trimmedText == text) }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toContainText: function () {
|
||||
return {
|
||||
compare: function (actual, text) {
|
||||
var trimmedText = $.trim($(actual).text())
|
||||
|
||||
if (text && $.isFunction(text.test)) {
|
||||
return { pass: text.test(trimmedText) }
|
||||
} else {
|
||||
return { pass: trimmedText.indexOf(text) != -1 }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toHaveValue: function () {
|
||||
return {
|
||||
compare: function (actual, value) {
|
||||
return { pass: $(actual).val() === value }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toHaveData: function () {
|
||||
return {
|
||||
compare: function (actual, key, expectedValue) {
|
||||
return { pass: hasProperty($(actual).data(key), expectedValue) }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toContainElement: function () {
|
||||
return {
|
||||
compare: function (actual, selector) {
|
||||
return { pass: $(actual).find(selector).length }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toBeMatchedBy: function () {
|
||||
return {
|
||||
compare: function (actual, selector) {
|
||||
return { pass: $(actual).filter(selector).length }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toBeDisabled: function () {
|
||||
return {
|
||||
compare: function (actual, selector) {
|
||||
return { pass: $(actual).is(':disabled') }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toBeFocused: function (selector) {
|
||||
return {
|
||||
compare: function (actual, selector) {
|
||||
return { pass: $(actual)[0] === $(actual)[0].ownerDocument.activeElement }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toHandle: function () {
|
||||
return {
|
||||
compare: function (actual, event) {
|
||||
if ( !actual || actual.length === 0 ) return { pass: false };
|
||||
var events = $._data($(actual).get(0), "events")
|
||||
|
||||
if (!events || !event || typeof event !== "string") {
|
||||
return { pass: false }
|
||||
}
|
||||
|
||||
var namespaces = event.split(".")
|
||||
, eventType = namespaces.shift()
|
||||
, sortedNamespaces = namespaces.slice(0).sort()
|
||||
, namespaceRegExp = new RegExp("(^|\\.)" + sortedNamespaces.join("\\.(?:.*\\.)?") + "(\\.|$)")
|
||||
|
||||
if (events[eventType] && namespaces.length) {
|
||||
for (var i = 0; i < events[eventType].length; i++) {
|
||||
var namespace = events[eventType][i].namespace
|
||||
|
||||
if (namespaceRegExp.test(namespace))
|
||||
return { pass: true }
|
||||
}
|
||||
} else {
|
||||
return { pass: (events[eventType] && events[eventType].length > 0) }
|
||||
}
|
||||
|
||||
return { pass: false }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toHandleWith: function () {
|
||||
return {
|
||||
compare: function (actual, eventName, eventHandler) {
|
||||
if ( !actual || actual.length === 0 ) return { pass: false };
|
||||
var normalizedEventName = eventName.split('.')[0]
|
||||
, stack = $._data($(actual).get(0), "events")[normalizedEventName]
|
||||
|
||||
for (var i = 0; i < stack.length; i++) {
|
||||
if (stack[i].handler == eventHandler) return { pass: true }
|
||||
}
|
||||
|
||||
return { pass: false }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toHaveBeenTriggeredOn: function () {
|
||||
return {
|
||||
compare: function (actual, selector) {
|
||||
var result = { pass: jasmine.jQuery.events.wasTriggered(selector, actual) }
|
||||
|
||||
result.message = result.pass ?
|
||||
"Expected event " + $(actual) + " not to have been triggered on " + selector :
|
||||
"Expected event " + $(actual) + " to have been triggered on " + selector
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toHaveBeenTriggered: function (){
|
||||
return {
|
||||
compare: function (actual) {
|
||||
var eventName = actual.eventName
|
||||
, selector = actual.selector
|
||||
, result = { pass: jasmine.jQuery.events.wasTriggered(selector, eventName) }
|
||||
|
||||
result.message = result.pass ?
|
||||
"Expected event " + eventName + " not to have been triggered on " + selector :
|
||||
"Expected event " + eventName + " to have been triggered on " + selector
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toHaveBeenTriggeredOnAndWith: function (j$, customEqualityTesters) {
|
||||
return {
|
||||
compare: function (actual, selector, expectedArgs) {
|
||||
var wasTriggered = jasmine.jQuery.events.wasTriggered(selector, actual)
|
||||
, result = { pass: wasTriggered && jasmine.jQuery.events.wasTriggeredWith(selector, actual, expectedArgs, j$, customEqualityTesters) }
|
||||
|
||||
if (wasTriggered) {
|
||||
var actualArgs = jasmine.jQuery.events.args(selector, actual, expectedArgs)[1]
|
||||
result.message = result.pass ?
|
||||
"Expected event " + actual + " not to have been triggered with " + jasmine.pp(expectedArgs) + " but it was triggered with " + jasmine.pp(actualArgs) :
|
||||
"Expected event " + actual + " to have been triggered with " + jasmine.pp(expectedArgs) + " but it was triggered with " + jasmine.pp(actualArgs)
|
||||
|
||||
} else {
|
||||
// todo check on this
|
||||
result.message = result.pass ?
|
||||
"Expected event " + actual + " not to have been triggered on " + selector :
|
||||
"Expected event " + actual + " to have been triggered on " + selector
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toHaveBeenPreventedOn: function () {
|
||||
return {
|
||||
compare: function (actual, selector) {
|
||||
var result = { pass: jasmine.jQuery.events.wasPrevented(selector, actual) }
|
||||
|
||||
result.message = result.pass ?
|
||||
"Expected event " + actual + " not to have been prevented on " + selector :
|
||||
"Expected event " + actual + " to have been prevented on " + selector
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toHaveBeenPrevented: function () {
|
||||
return {
|
||||
compare: function (actual) {
|
||||
var eventName = actual.eventName
|
||||
, selector = actual.selector
|
||||
, result = { pass: jasmine.jQuery.events.wasPrevented(selector, eventName) }
|
||||
|
||||
result.message = result.pass ?
|
||||
"Expected event " + eventName + " not to have been prevented on " + selector :
|
||||
"Expected event " + eventName + " to have been prevented on " + selector
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toHaveBeenStoppedOn: function () {
|
||||
return {
|
||||
compare: function (actual, selector) {
|
||||
var result = { pass: jasmine.jQuery.events.wasStopped(selector, actual) }
|
||||
|
||||
result.message = result.pass ?
|
||||
"Expected event " + actual + " not to have been stopped on " + selector :
|
||||
"Expected event " + actual + " to have been stopped on " + selector
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toHaveBeenStopped: function () {
|
||||
return {
|
||||
compare: function (actual) {
|
||||
var eventName = actual.eventName
|
||||
, selector = actual.selector
|
||||
, result = { pass: jasmine.jQuery.events.wasStopped(selector, eventName) }
|
||||
|
||||
result.message = result.pass ?
|
||||
"Expected event " + eventName + " not to have been stopped on " + selector :
|
||||
"Expected event " + eventName + " to have been stopped on " + selector
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
jasmine.getEnv().addCustomEqualityTester(function(a, b) {
|
||||
if (a && b) {
|
||||
if (a instanceof $ || jasmine.isDomNode(a)) {
|
||||
var $a = $(a)
|
||||
|
||||
if (b instanceof $)
|
||||
return $a.length == b.length && a.is(b)
|
||||
|
||||
return $a.is(b);
|
||||
}
|
||||
|
||||
if (b instanceof $ || jasmine.isDomNode(b)) {
|
||||
var $b = $(b)
|
||||
|
||||
if (a instanceof $)
|
||||
return a.length == $b.length && $b.is(a)
|
||||
|
||||
return $(b).is(a);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
jasmine.getEnv().addCustomEqualityTester(function (a, b) {
|
||||
if (a instanceof $ && b instanceof $ && a.size() == b.size())
|
||||
return a.is(b)
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
jasmine.getFixtures().cleanUp()
|
||||
jasmine.getStyleFixtures().cleanUp()
|
||||
jasmine.jQuery.events.cleanUp()
|
||||
})
|
||||
|
||||
window.readFixtures = function () {
|
||||
return jasmine.getFixtures().proxyCallTo_('read', arguments)
|
||||
}
|
||||
|
||||
window.preloadFixtures = function () {
|
||||
jasmine.getFixtures().proxyCallTo_('preload', arguments)
|
||||
}
|
||||
|
||||
window.loadFixtures = function () {
|
||||
jasmine.getFixtures().proxyCallTo_('load', arguments)
|
||||
}
|
||||
|
||||
window.appendLoadFixtures = function () {
|
||||
jasmine.getFixtures().proxyCallTo_('appendLoad', arguments)
|
||||
}
|
||||
|
||||
window.setFixtures = function (html) {
|
||||
return jasmine.getFixtures().proxyCallTo_('set', arguments)
|
||||
}
|
||||
|
||||
window.appendSetFixtures = function () {
|
||||
jasmine.getFixtures().proxyCallTo_('appendSet', arguments)
|
||||
}
|
||||
|
||||
window.sandbox = function (attributes) {
|
||||
return jasmine.getFixtures().sandbox(attributes)
|
||||
}
|
||||
|
||||
window.spyOnEvent = function (selector, eventName) {
|
||||
return jasmine.jQuery.events.spyOn(selector, eventName)
|
||||
}
|
||||
|
||||
window.preloadStyleFixtures = function () {
|
||||
jasmine.getStyleFixtures().proxyCallTo_('preload', arguments)
|
||||
}
|
||||
|
||||
window.loadStyleFixtures = function () {
|
||||
jasmine.getStyleFixtures().proxyCallTo_('load', arguments)
|
||||
}
|
||||
|
||||
window.appendLoadStyleFixtures = function () {
|
||||
jasmine.getStyleFixtures().proxyCallTo_('appendLoad', arguments)
|
||||
}
|
||||
|
||||
window.setStyleFixtures = function (html) {
|
||||
jasmine.getStyleFixtures().proxyCallTo_('set', arguments)
|
||||
}
|
||||
|
||||
window.appendSetStyleFixtures = function (html) {
|
||||
jasmine.getStyleFixtures().proxyCallTo_('appendSet', arguments)
|
||||
}
|
||||
|
||||
window.loadJSONFixtures = function () {
|
||||
return jasmine.getJSONFixtures().proxyCallTo_('load', arguments)
|
||||
}
|
||||
|
||||
window.getJSONFixture = function (url) {
|
||||
return jasmine.getJSONFixtures().proxyCallTo_('read', arguments)[url]
|
||||
}
|
||||
}));
|
Loading…
Reference in a new issue