diff options
Diffstat (limited to 'doc/development')
-rw-r--r-- | doc/development/README.md | 4 | ||||
-rw-r--r-- | doc/development/fe_guide/img/testing_triangle.png | bin | 0 -> 11836 bytes | |||
-rw-r--r-- | doc/development/fe_guide/index.md | 14 | ||||
-rw-r--r-- | doc/development/fe_guide/testing.md | 102 | ||||
-rw-r--r-- | doc/development/i18n_guide.md | 239 | ||||
-rw-r--r-- | doc/development/ux_guide/basics.md | 2 | ||||
-rw-r--r-- | doc/development/what_requires_downtime.md | 2 | ||||
-rw-r--r-- | doc/development/writing_documentation.md | 19 |
8 files changed, 357 insertions, 25 deletions
diff --git a/doc/development/README.md b/doc/development/README.md index 63db332b557..934c6849ff9 100644 --- a/doc/development/README.md +++ b/doc/development/README.md @@ -50,6 +50,10 @@ - [Post Deployment Migrations](post_deployment_migrations.md) - [Foreign Keys & Associations](foreign_keys.md) +## i18n + +- [Internationalization for GitLab](i18n_guide.md) + ## Compliance - [Licensing](licensing.md) for ensuring license compliance diff --git a/doc/development/fe_guide/img/testing_triangle.png b/doc/development/fe_guide/img/testing_triangle.png Binary files differnew file mode 100644 index 00000000000..7a9a848c2ee --- /dev/null +++ b/doc/development/fe_guide/img/testing_triangle.png diff --git a/doc/development/fe_guide/index.md b/doc/development/fe_guide/index.md index a08694fb66a..64bcb4a0257 100644 --- a/doc/development/fe_guide/index.md +++ b/doc/development/fe_guide/index.md @@ -45,15 +45,11 @@ should be `new-feature`. branch from `new-feature`, let's call it `new-feature-step-2` and repeat the process done before. ```shell -* master -|\ -| * new-feature -| |\ -| | * new-feature-step-1 -| |\ -| | * new-feature-step-2 -| |\ -| | * new-feature-step-3 +master +└─ new-feature + ├─ new-feature-step-1 + ├─ new-feature-step-2 + └─ new-feature-step-3 ``` **Tips** diff --git a/doc/development/fe_guide/testing.md b/doc/development/fe_guide/testing.md index a8c264bbd3c..0ef9fc61a61 100644 --- a/doc/development/fe_guide/testing.md +++ b/doc/development/fe_guide/testing.md @@ -1,11 +1,20 @@ # Frontend Testing -There are two types of tests you'll encounter while developing frontend code -at GitLab. We use Karma and Jasmine for JavaScript unit testing, and RSpec -feature tests with Capybara for integration testing. +There are two types of test suites you'll encounter while developing frontend code +at GitLab. We use Karma and Jasmine for JavaScript unit and integration testing, and RSpec +feature tests with Capybara for e2e (end-to-end) integration testing. -Feature tests need to be written for all new features. Regression tests ought -to be written for all bug fixes to prevent them from recurring in the future. +Unit and feature tests need to be written for all new features. +Most of the time, you should use rspec for your feature tests. +There are cases where the behaviour you are testing is not worth the time spent running the full application, +for example, if you are testing styling, animation or small actions that don't involve the backend, +you should write an integration test using Jasmine. + + + +_This diagram demonstrates the relative priority of each test type we use_ + +Regression tests should be written for bug fixes to prevent them from recurring in the future. See [the Testing Standards and Style Guidelines](../testing.md) for more information on general testing practices at GitLab. @@ -13,10 +22,12 @@ for more information on general testing practices at GitLab. ## Karma test suite GitLab uses the [Karma][karma] test runner with [Jasmine][jasmine] as its test -framework for our JavaScript unit tests. For tests that rely on DOM -manipulation, we generate HTML files using RSpec suites (see `spec/javascripts/fixtures/*.rb` for examples). +framework for our JavaScript unit and integration tests. For integration tests, +we generate HTML files using RSpec (see `spec/javascripts/fixtures/*.rb` for examples). Some fixtures are still HAML templates that are translated to HTML files using the same mechanism (see `static_fixtures.rb`). -Those will be migrated over time. +Adding these static fixtures should be avoided as they are harder to keep up to date with real views. +The existing static fixtures will be migrated over time. +Please see [gitlab-org/gitlab-ce#24753](https://gitlab.com/gitlab-org/gitlab-ce/issues/24753) to track our progress. Fixtures are served during testing by the [jasmine-jquery][jasmine-jquery] plugin. JavaScript tests live in `spec/javascripts/`, matching the folder structure @@ -28,7 +39,9 @@ browser and you will not have access to certain APIs, such as [`Notification`](https://developer.mozilla.org/en-US/docs/Web/API/notification), which will have to be stubbed. -### Writing tests +### Best practice + +#### Naming unit tests When writing describe test blocks to test specific functions/methods, please use the method name as the describe block name. @@ -55,6 +68,77 @@ describe('.methodName', () => { }); }); ``` +#### Testing Promises + +When testing Promises you should always make sure that the test is asynchronous and rejections are handled. +Your Promise chain should therefore end with a call of the `done` callback and `done.fail` in case an error occurred. + +```javascript +// Good +it('tests a promise', (done) => { + promise + .then((data) => { + expect(data).toBe(asExpected); + }) + .then(done) + .catch(done.fail); +}); + +// Good +it('tests a promise rejection', (done) => { + promise + .then(done.fail) + .catch((error) => { + expect(error).toBe(expectedError); + }) + .then(done) + .catch(done.fail); +}); + +// Bad (missing done callback) +it('tests a promise', () => { + promise + .then((data) => { + expect(data).toBe(asExpected); + }) +}); + +// Bad (missing catch) +it('tests a promise', (done) => { + promise + .then((data) => { + expect(data).toBe(asExpected); + }) + .then(done) +}); + +// Bad (use done.fail in asynchronous tests) +it('tests a promise', (done) => { + promise + .then((data) => { + expect(data).toBe(asExpected); + }) + .then(done) + .catch(fail) +}); + +// Bad (missing catch) +it('tests a promise rejection', (done) => { + promise + .catch((error) => { + expect(error).toBe(expectedError); + }) + .then(done) +}); +``` + +#### Stubbing + +For unit tests, you should stub methods that are unrelated to the current unit you are testing. +If you need to use a prototype method, instantiate an instance of the class and call it there instead of mocking the instance completely. + +For integration tests, you should stub methods that will effect the stability of the test if they +execute their original behaviour. i.e. Network requests. ### Vue.js unit tests See this [section][vue-test]. diff --git a/doc/development/i18n_guide.md b/doc/development/i18n_guide.md new file mode 100644 index 00000000000..44eca68aaca --- /dev/null +++ b/doc/development/i18n_guide.md @@ -0,0 +1,239 @@ +# Internationalization for GitLab + +> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10669) in GitLab 9.2. + +For working with internationalization (i18n) we use +[GNU gettext](https://www.gnu.org/software/gettext/) given it's the most used +tool for this task and we have a lot of applications that will help us to work +with it. + +## Tools + +We use a couple of gems: + +1. [`gettext_i18n_rails`](https://github.com/grosser/gettext_i18n_rails): this + gem allow us to translate content from models, views and controllers. Also + it gives us access to the following raketasks: + - `rake gettext:find`: Parses almost all the files from the + Rails application looking for content that has been marked for + translation. Finally, it updates the PO files with the new content that + it has found. + - `rake gettext:pack`: Processes the PO files and generates the + MO files that are binary and are finally used by the application. + +1. [`gettext_i18n_rails_js`](https://github.com/webhippie/gettext_i18n_rails_js): + this gem is useful to make the translations available in JavaScript. It + provides the following raketask: + - `rake gettext:po_to_json`: Reads the contents from the PO files and + generates JSON files containing all the available translations. + +1. PO editor: there are multiple applications that can help us to work with PO + files, a good option is [Poedit](https://poedit.net/download) which is + available for macOS, GNU/Linux and Windows. + +## Preparing a page for translation + +We basically have 4 types of files: + +1. Ruby files: basically Models and Controllers. +1. HAML files: these are the view files. +1. ERB files: used for email templates. +1. JavaScript files: we mostly need to work with VUE JS templates. + +### Ruby files + +If there is a method or variable that works with a raw string, for instance: + +```ruby +def hello + "Hello world!" +end +``` + +Or: + +```ruby +hello = "Hello world!" +``` + +You can easily mark that content for translation with: + +```ruby +def hello + _("Hello world!") +end +``` + +Or: + +```ruby +hello = _("Hello world!") +``` + +### HAML files + +Given the following content in HAML: + +```haml +%h1 Hello world! +``` + +You can mark that content for translation with: + +```haml +%h1= _("Hello world!") +``` + +### ERB files + +Given the following content in ERB: + +```erb +<h1>Hello world!</h1> +``` + +You can mark that content for translation with: + +```erb +<h1><%= _("Hello world!") %></h1> +``` + +### JavaScript files + +In JavaScript we added the `__()` (double underscore parenthesis) function +for translations. + +### Updating the PO files with the new content + +Now that the new content is marked for translation, we need to update the PO +files with the following command: + +```sh +bundle exec rake gettext:find +``` + +This command will update the `locale/**/gitlab.edit.po` file with the +new content that the parser has found. + +New translations will be added with their default content and will be marked +fuzzy. To use the translation, look for the `#, fuzzy` mention in `gitlab.edit.po` +and remove it. + +Translations that aren't used in the source code anymore will be marked with +`~#`; these can be removed to keep our translation files clutter-free. + +## Working with special content + +### Interpolation + +- In Ruby/HAML: + + ```ruby + _("Hello %{name}") % { name: 'Joe' } + ``` + +- In JavaScript: Not supported at this moment. + +### Plurals + +- In Ruby/HAML: + + ```ruby + n_('Apple', 'Apples', 3) => 'Apples' + ``` + + Using interpolation: + ```ruby + n_("There is a mouse.", "There are %d mice.", size) % size + ``` + +- In JavaScript: + + ```js + n__('Apple', 'Apples', 3) => 'Apples' + ``` + + Using interpolation: + + ```js + n__('Last day', 'Last %d days', 30) => 'Last 30 days' + ``` + +### Namespaces + +Sometimes you need to add some context to the text that you want to translate +(if the word occurs in a sentence and/or the word is ambiguous). + +- In Ruby/HAML: + + ```ruby + s_('OpenedNDaysAgo|Opened') + ``` + + In case the translation is not found it will return `Opened`. + +- In JavaScript: + + ```js + s__('OpenedNDaysAgo|Opened') + ``` + +### Just marking content for parsing + +Sometimes there are some dynamic translations that can't be found by the +parser when running `bundle exec rake gettext:find`. For these scenarios you can +use the [`_N` method](https://github.com/grosser/gettext_i18n_rails/blob/c09e38d481e0899ca7d3fc01786834fa8e7aab97/Readme.md#unfound-translations-with-rake-gettextfind). + +There is also and alternative method to [translate messages from validation errors](https://github.com/grosser/gettext_i18n_rails/blob/c09e38d481e0899ca7d3fc01786834fa8e7aab97/Readme.md#option-a). + +## Adding a new language + +Let's suppose you want to add translations for a new language, let's say French. + +1. The first step is to register the new language in `lib/gitlab/i18n.rb`: + + ```ruby + ... + AVAILABLE_LANGUAGES = { + ..., + 'fr' => 'Français' + }.freeze + ... + ``` + +1. Next, you need to add the language: + + ```sh + bundle exec rake gettext:add_language[fr] + ``` + + If you want to add a new language for a specific region, the command is similar, + you just need to separate the region with an underscore (`_`). For example: + + ```sh + bundle exec rake gettext:add_language[en_gb] + ``` + +1. Now that the language is added, a new directory has been created under the + path: `locale/fr/`. You can now start using your PO editor to edit the PO file + located in: `locale/fr/gitlab.edit.po`. + +1. After you're done updating the translations, you need to process the PO files + in order to generate the binary MO files and finally update the JSON files + containing the translations: + + ```sh + bundle exec rake gettext:pack + bundle exec rake gettext:po_to_json + ``` + +1. In order to see the translated content we need to change our preferred language + which can be found under the user's **Settings** (`/profile`). + +1. After checking that the changes are ok, you can proceed to commit the new files. + For example: + + ```sh + git add locale/fr/ app/assets/javascripts/locale/fr/ + git commit -m "Add French translations for Cycle Analytics page" + ``` diff --git a/doc/development/ux_guide/basics.md b/doc/development/ux_guide/basics.md index 259b214bd59..a436e9b1948 100644 --- a/doc/development/ux_guide/basics.md +++ b/doc/development/ux_guide/basics.md @@ -22,7 +22,7 @@ GitLab's main typeface used throughout the UI is **Source Sans Pro**. We support ### Monospace typeface -This is the typeface used for code blocks. GitLab uses the OS default font. +This is the typeface used for code blocks and references to commits, branches, and tags (`.commit-sha` or `.ref-name`). GitLab uses the OS default font. - **Menlo** (Mac) - **Consolas** (Windows) - **Liberation Mono** (Linux) diff --git a/doc/development/what_requires_downtime.md b/doc/development/what_requires_downtime.md index 8da6ad684f5..c4830322fa8 100644 --- a/doc/development/what_requires_downtime.md +++ b/doc/development/what_requires_downtime.md @@ -139,6 +139,8 @@ Adding or removing a NOT NULL clause (or another constraint) can typically be done without requiring downtime. However, this does require that any application changes are deployed _first_. Thus, changing the constraints of a column should happen in a post-deployment migration. +NOTE: Avoid using `change_column` as it produces inefficient query because it re-defines +the whole column type. For example, to add a NOT NULL constraint, prefer `change_column_null ` ## Changing Column Types diff --git a/doc/development/writing_documentation.md b/doc/development/writing_documentation.md index 657a826d7ee..eac9ec2a470 100644 --- a/doc/development/writing_documentation.md +++ b/doc/development/writing_documentation.md @@ -78,14 +78,21 @@ Currently GitLab docs use Redcarpet as [markdown](../user/markdown.md) engine, b We try to treat documentation as code, thus have implemented some testing. Currently, the following tests are in place: -1. `docs:check:links`: Check that all internal (relative) links work correctly -1. `docs:check:apilint`: Check that the API docs follow some conventions +1. `docs lint`: Check that all internal (relative) links work correctly and + that all cURL examples in API docs use the full switches. If your contribution contains **only** documentation changes, you can speed up -the CI process by prepending to the name of your branch: `docs/`. For example, -a valid name would be `docs/update-api-issues` and it will run only the docs -tests. If the name is `docs-update-api-issues`, the whole test suite will run -(including docs). +the CI process by following some branch naming conventions. You have three +choices: + +| Branch name | Valid example | +| ----------- | ------------- | +| Starting with `docs/` | `docs/update-api-issues` | +| Starting with `docs-` | `docs-update-api-issues` | +| Ending in `-docs` | `123-update-api-issues-docs` | + +If your branch name matches any of the above, it will run only the docs +tests. If it doesn't, the whole test suite will run (including docs). --- |