Repository: vuejs/vue
Pull request: fix(provide): Merges symbol provides

Fixes merging multiple provides using Symbols

fix #7923

<!--
Please make sure to read the Pull Request Guidelines:
https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#pull-request-guidelines
-->

<!-- PULL REQUEST TEMPLATE -->
<!-- (Update "[ ]" to "[x]" to check a box) -->

**What kind of change does this PR introduce?** (check at least one)

- [x] Bugfix
- [ ] Feature
- [ ] Code style update
- [ ] Refactor
- [ ] Build-related changes
- [ ] Other, please describe:

**Does this PR introduce a breaking change?** (check one)

- [ ] Yes
- [x] No

If yes, please describe the impact and migration path for existing applications:

**The PR fulfills these requirements:**

- [x] It's submitted to the `dev` branch for v2.x (or to a previous version branch), _not_ the `master` branch
- [x] When resolving a specific issue, it's referenced in the PR's title (e.g. `fix #xxx[,#xxx]`, where "xxx" is the issue number)
- [ ] All tests are passing: https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#development-setup
- [x] New/updated tests are included

If adding a **new feature**, the PR's description includes:
- [ ] A convincing reason for adding this feature (to avoid wasting your time, it's best to open a suggestion issue first and wait for approval before working on it)

**Other information:**
The test I added only passes in an environment with symbols (since it's handling a case that fails with symbols). This fails when the test is executed with Phantom, since it doesn't have Symbol support natively or with a polyfill, but passes in Chrome.

I can add babel-polyfill but seemed excessive for one test. Wanted to know how to move forward with that one before making any drastic changes to making it work.

Changed files:
filename: src/core/util/options.js
patch: @@ -4,6 +4,7 @@ import config from '../config'
 import { warn } from './debug'
 import { nativeWatch } from './env'
 import { set } from '../observer/index'
+import { hasSymbol } from '../util/index'
 
 import {
   ASSET_TYPES,
@@ -48,9 +49,15 @@ if (process.env.NODE_ENV !== 'production') {
 function mergeData (to: Object, from: ?Object): Object {
   if (!from) return to
   let key, toVal, fromVal
-  const keys = Object.keys(from)
+
+  const keys = hasSymbol
+    ? Reflect.ownKeys(from)
+    : Object.keys(from)
+
   for (let i = 0; i < keys.length; i++) {
     key = keys[i]
+    // in case the object is already observed...
+    if (key === '__ob__') continue
     toVal = to[key]
     fromVal = from[key]
     if (!hasOwn(to, key)) {

filename: test/unit/features/options/inject.spec.js
patch: @@ -188,6 +188,32 @@ describe('Options provide/inject', () => {
       }).$mount()
       expect(vm.$el.textContent).toBe('123')
     })
+
+    it('should merge symbol provide from mixins (functions)', () => {
+      const keyA = Symbol('foo')
+      const keyB = Symbol('bar')
+
+      const mixinA = { provide: () => ({ [keyA]: 'foo' }) }
+      const mixinB = { provide: () => ({ [keyB]: 'bar' }) }
+      const child = {
+        inject: {
+          foo: keyA,
+          bar: keyB
+        },
+        template: `<span/>`,
+        created () {
+          injected = [this.foo, this.bar]
+        }
+      }
+      new Vue({
+        mixins: [mixinA, mixinB],
+        render (h) {
+          return h(child)
+        }
+      }).$mount()
+
+      expect(injected).toEqual(['foo', 'bar'])
+    })
   }
 
   // GitHub issue #5223