Jsdom: Exponer algún método agregar archivos a una lista de archivos

Creado en 23 oct. 2015  ·  30Comentarios  ·  Fuente: jsdom/jsdom

FileList no se puede escribir en las especificaciones, pero para hacer que input.files comprobable, necesitaremos implementar un método útil para modificarlo.

/ cc @cpojer

feature

Comentario más útil

Logré crear FileList sin tener que alterar ninguno de los códigos de la biblioteca jsdom:

const createFile = (size = 44320, name = 'ecp-logo.png', type = 'image/png') =>
  new File([new ArrayBuffer(size)], name , {
    type: type,
  });

const createFileList = (file) => {
  const fileList = new FileList();
  fileList[0] = file;
  return fileList;
}

const fileList = createFileList(createFile());

En este caso, estoy creando un constructor de llamada de objeto FileList en FileList proporcionado por jsdom. Después de eso, solo estoy agregando un archivo a esa lista usando la notación Array. En mi caso, solo necesito 1 archivo en la matriz, pero esto también se puede cambiar a for loop para agregar varios archivos a FileList. Estoy creando un archivo con nombre / tipo / tamaño personalizado a través del constructor de archivos también proporcionado por jsdom, cuya funcionalidad sigue la especificación.

Esto podría ser útil para alguien que esté buscando cómo simular FileList en el entorno jsdom, pero todavía hay un problema. Crear FileList con una matriz de archivos usando este método no permitiría obtener elementos de la matriz usando el método FileList.item (index). Pero, incluso eso se puede arreglar anulando su método algo como esto:

const createFileList = (file) => {
  const fileList = new FileList();
  fileList[0] = file;
  fileList.item = index => fileList[index]; // override method functionality
  return fileList;
}

Sigo sintiendo que sería mejor si jsdom pudiera ofrecer estas funcionalidades para propósitos de prueba desde el primer momento.

Todos 30 comentarios

input.files = createFileList(file1, file2, ...) estaría bien.

@cpojer ¿Está generando objetos File reales para ponerlos allí?

Hacer que input.files se pueda escribir es probablemente malo, probablemente podamos devolver la matriz sin procesar para completarlo usted mismo o hacer algo como fillFileList(input.files, [file]) .

En realidad, solo usamos datos simulados, por lo que completamos .files con una matriz de objetos. Pero creo que requerir que sea un objeto File sería razonable.

¿Parece que defineProperty estaría bien aquí? Las propiedades DOM son reconfigurables por una razón ...

Sin embargo, prefiero crear una FileList real con datos reales.

También debería ser posible acceder a los elementos de FileList utilizando sus índices de matriz. .item no es la única forma de acceder a sus campos.

Bien, parece que hay algunos problemas potenciales:

  • FileList no funciona correctamente con el acceso indexado (error)
  • No hay forma de crear objetos FileList para realizar pruebas (brecha de funciones de la plataforma web).

Pero modificar inputEl.files no es el problema.

Sí, quiero decir que no es genial decirle a los ingenieros que usen Object.defineProperty en una asignación regular, pero puedo vivir con eso.

Bueno, tienen que hacer eso de todos modos en un navegador real, así que me parece razonable ...

Hola, veo que esto tiene más de un año, me gustaría preguntar si hay algún progreso con respecto a este problema. Utilizo el marco Jest para probar mi aplicación React / Redux que internamente usa jsdom. Tengo un problema en el que necesito crear FileList dinámicamente con uno o más objetos File.

Mirando lib / jsdom / living / filelist.js puedo ver que hay un constructor para FileList pero no hay ninguna opción para pasarle archivos. Entiendo que FileList y File no tienen constructor de acuerdo con las especificaciones debido a razones de seguridad, pero ¿hay alguna intención de permitir que el constructor acepte una matriz de objetos File o al menos un método adicional (digamos _setItem_) que nos permita agregar File objetos en la lista específicamente con fines de prueba?

También veo otro problema con FileList. Si no me equivoco, debería ser un objeto tipo Array, igual que NodeList (lib / jsdom / living / node-list.js), lo que significa que debería existir la posibilidad de acceder a los objetos File de dos formas:

var fileList = document.getElementById("myfileinput").files;

fileList[0];
fileList.item(0);

Actualmente, solo es posible acceder a través del método. Esto significa que aquí se debe aplicar la misma lógica que en NodeList:

FileList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];

y los archivos deberían almacenarse de esta manera si permitiéramos pasar una matriz de archivos al constructor:

for (let i = 0; i < files.length; ++i) {
  this[i] = files[i];
}

No tengo opiniones sólidas sobre cómo se debe hacer esto, estos son solo ejemplos que utilizo para explicar mejor lo que estoy tratando de señalar.

Preguntas adicionales (no quiero abrir problemas innecesarios antes de preguntar aquí):

1.) ¿Sería beneficioso agregar el constructor de archivos para fines de prueba que crea un objeto de archivo a partir de un archivo real utilizando la ruta proporcionada como cadena? Encontré la biblioteca (lo siento, ya no puedo encontrar el enlace) que permitía esto:

const file = new File('../fixtures/files/test-image.png');

Este archivo creado para mí con propiedades (tamaño, última modificación, tipo ...) sin que tenga que crearlo manualmente:

const file = new File([''], 'test-image.png', {
  lastModified: 1449505890000,
  lastModifiedDate: new Date(1449505890000),
  name: "ecp-logo.png",
  size: 44320,
  type: "image/png",
});

No sé cómo funcionó esta biblioteca, todo lo que sé es que no se mantuvo durante más de un año y dejamos de usarla. Parece que ya no puedo encontrarlo.

2.) window.URL.createObjectURL no es compatible con jsdom. No estoy seguro de si debería informarse.

Logré crear FileList sin tener que alterar ninguno de los códigos de la biblioteca jsdom:

const createFile = (size = 44320, name = 'ecp-logo.png', type = 'image/png') =>
  new File([new ArrayBuffer(size)], name , {
    type: type,
  });

const createFileList = (file) => {
  const fileList = new FileList();
  fileList[0] = file;
  return fileList;
}

const fileList = createFileList(createFile());

En este caso, estoy creando un constructor de llamada de objeto FileList en FileList proporcionado por jsdom. Después de eso, solo estoy agregando un archivo a esa lista usando la notación Array. En mi caso, solo necesito 1 archivo en la matriz, pero esto también se puede cambiar a for loop para agregar varios archivos a FileList. Estoy creando un archivo con nombre / tipo / tamaño personalizado a través del constructor de archivos también proporcionado por jsdom, cuya funcionalidad sigue la especificación.

Esto podría ser útil para alguien que esté buscando cómo simular FileList en el entorno jsdom, pero todavía hay un problema. Crear FileList con una matriz de archivos usando este método no permitiría obtener elementos de la matriz usando el método FileList.item (index). Pero, incluso eso se puede arreglar anulando su método algo como esto:

const createFileList = (file) => {
  const fileList = new FileList();
  fileList[0] = file;
  fileList.item = index => fileList[index]; // override method functionality
  return fileList;
}

Sigo sintiendo que sería mejor si jsdom pudiera ofrecer estas funcionalidades para propósitos de prueba desde el primer momento.

Hola tios ,
Me estoy enfrentando a un problema mientras me burlo del objeto $ ("# selectorID of Upload file") [0] .files [0] que está devolviendo FileList.
¿Alguien puede ayudarme a crear un objeto FileList? Porque no puedo encontrar ningún enlace de referencia en ninguna parte de WWW

Actualmente no es posible.

Gracias Domenic,

Necesito escribir un caso de prueba para el evento de cambio de carga de archivos. cualquier forma alternativa de ejecutar ese escenario de prueba.

¿Algún progreso en esto?

@niksajanjic ¿
Ref: const file = new File('../fixtures/files/test-image.png');
¿O algo parecido?
Mejor

@domenic Parece ser un poco disidente en esa publicación de Twitter sobre el tema.

@domenic Ok, he configurado el comienzo básico para esto. No todos los controles están ahí, pero es esencialmente de lo que estaba hablando @niksajanjic .

crea un archivo

function createFile(file_path) {
  const { mtimeMs: lastModified, size } = fs.statSync(file_path)

  return new File(
    [new fs.readFileSync(file_path)],
    path.basename(file_path),
    {
      lastModified,
      type: mime.lookup(file_path) || '',
    }
  )
}

addFileList

function addFileList(input, file_paths) {
  if (typeof file_paths === 'string')
    file_paths = [file_paths]
  else if (!Array.isArray(file_paths)) {
    throw new Error('file_paths needs to be a file path string or an Array of file path strings')
  }

  const file_list = file_paths.map(fp => createFile(fp))
  file_list.__proto__ = Object.create(FileList.prototype)

  Object.defineProperty(input, 'files', {
    value: file_list,
    writeable: false,
  })

  return input
}

Archivo de demostración

/*eslint-disable no-console, no-unused-vars */

/*
https://github.com/jsdom/jsdom/issues/1272
*/

const fs = require('fs')
const path = require('path')
const mime = require('mime-types')

const { JSDOM } = require('jsdom')
const dom = new JSDOM(`
<!DOCTYPE html>
<body>
  <input type="file">
</body>
`)

const { window } = dom
const { document, File, FileList } = window


const file_paths = [
  '/Users/williamrusnack/Documents/form_database/test/try-input-file.html',
  '/Users/williamrusnack/Documents/form_database/test/try-jsdom-input-file.js',
]

function createFile(file_path) {
  const { mtimeMs: lastModified, size } = fs.statSync(file_path)

  return new File(
    [new fs.readFileSync(file_path)],
    path.basename(file_path),
    {
      lastModified,
      type: mime.lookup(file_path) || '',
    }
  )
}

function addFileList(input, file_paths) {
  if (typeof file_paths === 'string')
    file_paths = [file_paths]
  else if (!Array.isArray(file_paths)) {
    throw new Error('file_paths needs to be a file path string or an Array of file path strings')
  }

  const file_list = file_paths.map(fp => createFile(fp))
  file_list.__proto__ = Object.create(FileList.prototype)

  Object.defineProperty(input, 'files', {
    value: file_list,
    writeable: false,
  })

  return input
}



const input = document.querySelector('input')

addFileList(input, file_paths)

for (let i = 0; i < input.files.length; ++i) {
  const file = input.files[i]
  console.log('file', file)
  console.log('file.name', file.name)
  console.log('file.size', file.size)
  console.log('file.type', file.type)
  console.log('file.lastModified', file.lastModified)
  console.log()
}

@BebeSparkelSparkel Hice la prueba de la manera que expliqué en mi publicación posterior. Desafortunadamente, unos meses después dejó de funcionar en versiones más nuevas de jsdom cuando uno de mis colegas intentó copiar ese código. Entonces, tuvimos que comentar las pruebas para FileList en ese momento. A partir de ahí, no pudimos encontrar la forma de escribir esas pruebas y en los últimos meses nadie tuvo tiempo de echarle otro vistazo y tratar de encontrar la forma.

@niksajanjic Gracias por tu actualización. Estoy trabajando en la solución que propuso y parece estar funcionando (consulte el código anterior), pero creo que es poco probable que se agregue a jsdom ya que sería muy difícil averiguar cómo agregarlo.
Siéntase libre de echarle un vistazo y usarlo si lo desea.

Creó un script de ayuda simple que resuelve este problema hasta que el proyecto jsdom encuentre una solución.
https://bitbucket.org/william_rusnack/addfilelist/src/master/

Agregar un archivo:

const input = document.querySelector('input[type=file]')
addFileList(input, 'path/to/file')

Agregar varios archivos:

const input = document.querySelector('input[type=file]')
addFileList(input, [
  'path/to/file',
  'path/to/another/file',
  // add as many as you want
])

Instalar y requerir

npm install https://github.com/BebeSparkelSparkel/addFileList.git

`` `javascript
const {addFileList} = require ('addFileList')

## Functions
**addFileList**(input, file_paths)  
Effects: puts the file_paths as File object into input.files as a FileList  
Returns: input  
Arguments:  
- input: HTML input element  
- file_paths: String or Array of string file paths to put in input.files  
`const { addFileList } = require('addFileList')`  

## Example
Extract from example.js
```javascript
// add a single file
addFileList(input, 'example.js')

// log input's FileList
console.log(input.files)

// log file properties
const [ file ] = input.files
console.log(file)
console.log(
  '\nlastModified', file.lastModified,
  '\nname', file.name,
  '\nsize', file.size,
  '\ntype', file.type,
  '\n'
)

Resultado

$ node example.js 
FileList [ File {} ]
File {}

lastModified 1518523506000 
name example.js 
size 647 
type application/javascript 

@BebeSparkelSparkel parece que su repositorio ha sido eliminado?

Tuve un problema similar: escribí una función que tomaba FileList como entrada y quería escribir una prueba unitaria para ella usando jest. Pude usar la siguiente función auxiliar para falsificar un objeto FileList suficiente como para trabajar con mi función. La sintaxis es ES6 con anotaciones de flujo. No promete que funcionará en todas las situaciones, ya que solo está fingiendo la funcionalidad de la clase FileList real ...

const createFileList = (files: Array<File>): FileList => {
  return {
    length: files.length,
    item: (index: number) => files[index],
    * [Symbol.iterator]() {
      for (let i = 0; i < files.length; i++) {
        yield files[i];
      }
    },
    ...files,
  };
};

En la interfaz, puedo hacer algo como lo siguiente para (de manera indirecta) construir un FileList 'real':

export const makeFileList = files => {
  const reducer = (dataTransfer, file) => {
    dataTransfer.items.add(file)
    return dataTransfer
  }

  return files.reduce(reducer, new DataTransfer()).files
}

Ref: https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer

Desafortunadamente, jsdom no parece ser compatible con DataTransfer todavía, por lo que esto no funciona en mis pruebas:

Otras referencias:


Buscando un poco la fuente encontré exports.FileList = require("./generated/FileList").interface;

~ Pero no me quedó claro en GitHub dónde encontrar la fuente que termina construyendo ./generated ~

La exportación principal del paquete npm es ./lib/api.js y exporta una API pública muy pequeña:

exports.JSDOM = JSDOM;
exports.VirtualConsole = VirtualConsole;
exports.CookieJar = CookieJar;
exports.ResourceLoader = ResourceLoader;
exports.toughCookie = toughCookie;

Pero mirando en mi directorio ./node_modules/jsdom/lib/jsdom ... puedo ver que todos los archivos internos / de implementación también están allí, incluidos ./node_modules/jsdom/lib/jsdom/living/file-api :

Blob-impl.js  File-impl.js  FileList-impl.js  FileReader-impl.js

FileList-impl.js aquí contiene la implementación de JS real que respalda la api FileList expuesta en jsdom.

Ahora, si miramos ./node_modules/jsdom/lib/jsdom/living/generated/FileList.js , vemos la 'API pública' generada real que terminamos viendo a través del uso normal, incluido nuestro demasiado familiar:

class FileList {
  constructor() {
    throw new TypeError("Illegal constructor");
  }

Este archivo exporta module.exports = iface; , que contiene mucha más funcionalidad de la que obtenemos a través de la exposición de API pública 'normal', que solo usa la clave iface.interface . Entonces, quizás podríamos hacer algo divertido si usamos require("./generated/FileList") directamente. Eliminando los detalles de implementación, tenemos una interfaz que se parece a:

const iface = {
  _mixedIntoPredicates: [],
  is(obj) {..snip..},
  isImpl(obj) {..snip..},
  convert(obj, { context = "The provided value" } = {}) {..snip..},
  create(constructorArgs, privateData) {..snip..},
  createImpl(constructorArgs, privateData) {..snip..},
  _internalSetup(obj) {},
  setup(obj, constructorArgs, privateData) {...snip...},
  interface: FileList,
  expose: {
    Window: { FileList },
    Worker: { FileList }
  }
}; // iface

Entonces, ahora que sabemos que hay más poder para obtener ... veamos cómo otras áreas de jsdom acceden a él ...

Echando un vistazo a HTMLInputElement-impl , parece usar FileList.createImpl() , aunque desafortunadamente no nos muestra cómo usar los parámetros:

createImpl es solo un pequeño envoltorio alrededor del setup en el iface exportado:

createImpl(constructorArgs, privateData) {
    let obj = Object.create(FileList.prototype);
    obj = this.setup(obj, constructorArgs, privateData);
    return utils.implForWrapper(obj);
  },

Jugando con esto en una consola parece que tenemos la API expresivo del Array elemento de la FileListImpl está respaldado por. Entonces podemos hacer cosas como:

var flist = require('./node_modules/jsdom/lib/jsdom/living/generated/FileList.js')
var myFileListImpl = flist.createImpl()
myFileListImpl.push('aa')

Tiene una propiedad Symbol(wrapper) , a la que necesitaremos usar ./node_modules/jsdom/lib/jsdom/living/generated/utils.js:37 para acceder:

var utils = require('./node_modules/jsdom/lib/jsdom/living/generated/utils.js')
var wrapper = myFileListImpl[utils.wrapperSymbol]

El iface exportado tiene una función convert , que throw new TypeError( $ {context} no es del tipo 'FileList'. ); cuando el objeto proporcionado no es FileList . Podemos usar esto para probar cosas.

Si lo llamamos en el crudo myFileListImpl arroja el error:

flist.convert(myFileListImpl)

Mientras que al usar wrapper que extrajimos anteriormente, podemos ver que no arroja el error:

flist.convert(myFileListImpl[utils.wrapperSymbol])

Con esto, podemos modificar myFileListImpl , y recuperar un objeto FileList aceptable, para pasarlo a donde lo necesitemos. Un ejemplo completamente trabajado (usando util.wrapperForImpl() lugar de nuestro código anterior):

var _FileList = require('./node_modules/jsdom/lib/jsdom/living/generated/FileList.js')
var utils = require('./node_modules/jsdom/lib/jsdom/living/generated/utils.js')

var myMutableFileListImpl = _FileList.createImpl()

myMutableFileListImpl.length // 0
myMutableFileListImpl.push(new File([], 'a.jpg'))
myMutableFileListImpl.length // 1

var myFileList = utils.wrapperForImpl(myMutableFileListImpl)
_FileList.convert(myFileList) // no error

myFileList.length // 1
myFileList[0] // the File{} object

Ahora, con ese conocimiento, puedo implementar la versión de prueba jsdom de mi truco de solución original del navegador (implementado sobre ImmutableJS para la pereza):

import { Map, Record } from 'immutable'

import jsdomFileList from 'jsdom/lib/jsdom/living/generated/FileList'
import { wrapperForImpl } from 'jsdom/lib/jsdom/living/generated/utils'

// Note: relying on internal API's is super hacky, and will probably break
// As soon as we can, we should use whatever the proper outcome from this issue is:
//   https://github.com/jsdom/jsdom/issues/1272#issuecomment-486088445

export const makeFileList = files => {
  const reducer = (fileListImpl, file) => {
    fileListImpl.push(file)
    return fileListImpl
  }

  const fileListImpl = files.reduce(reducer, jsdomFileList.createImpl())

  return wrapperForImpl(fileListImpl)
}

export class DataTransferStub extends Record({ items: Map() }) {
  get files() {
    return makeFileList(this.items.toList().toArray())
  }
}

export const stubGlobalDataTransfer = () => {
  global.DataTransfer = DataTransferStub
}

export const restoreGlobalDataTransfer = () => {
  global.DataTransfer = undefined
}

Y luego conectarlo manualmente a mis vars globales para que mis pruebas ava puedan usarlo:

import {
  restoreGlobalDataTransfer,
  stubGlobalDataTransfer,
} from ../helpers/jsdom-helpers'

test.before(t => {
  stubGlobalDataTransfer()
})

test.after(t => {
  restoreGlobalDataTransfer()
})

Todos los navegadores modernos (es decir, no IE <= 11) ahora admiten la configuración de archivos de entrada en una lista de archivos https://stackoverflow.com/a/47522812/2744776

Esto parece haber cambiado en una versión reciente. Esto es lo que funcionó para mí:

const jsdomUtils = require('jsdom/lib/jsdom/living/generated/utils');
const jsdomFileList = require('jsdom/lib/jsdom/living/generated/FileList');
function makeFileList(...files) {
    const impl = jsdomFileList.createImpl(window);
    const ret = Object.assign([...files], {
        item: (ix) => ret[ix],
        [jsdomUtils.implSymbol]: impl,
    });
    impl[jsdomUtils.wrapperSymbol] = ret;
    Object.setPrototypeOf(ret, FileList.prototype);
    return ret;
}

Si está utilizando JSDOM a través de Jest, debe asegurarse de requerir los componentes internos fuera de la máquina virtual de prueba. Creé un entorno de prueba personalizado como este:

const JsdomEnvironment = require('jest-environment-jsdom');

/** See jsdom/jsdom#1272 */
class EnvWithSyntheticFileList extends JsdomEnvironment {
    async setup() {
        await super.setup();
        this.global.jsdomUtils = require('jsdom/lib/jsdom/living/generated/utils');
        this.global.jsdomFileList = require('jsdom/lib/jsdom/living/generated/FileList');
    }
}

module.exports = EnvWithSyntheticFileList;

Para poder acceder a las importaciones 'externas'.

Esto parece haber cambiado en una versión reciente. Esto es lo que funcionó para mí:

const jsdomUtils = require('jsdom/lib/jsdom/living/generated/utils');
const jsdomFileList = require('jsdom/lib/jsdom/living/generated/FileList');
function makeFileList(...files) {
    const impl = jsdomFileList.createImpl(window);
    const ret = Object.assign([...files], {
        item: (ix) => ret[ix],
        [jsdomUtils.implSymbol]: impl,
    });
    impl[jsdomUtils.wrapperSymbol] = ret;
    Object.setPrototypeOf(ret, FileList.prototype);
    return ret;
}

Si está utilizando JSDOM a través de Jest, debe asegurarse de requerir los componentes internos fuera de la máquina virtual de prueba. Creé un entorno de prueba personalizado como este:

const JsdomEnvironment = require('jest-environment-jsdom');

/** See jsdom/jsdom#1272 */
class EnvWithSyntheticFileList extends JsdomEnvironment {
    async setup() {
        await super.setup();
        this.global.jsdomUtils = require('jsdom/lib/jsdom/living/generated/utils');
        this.global.jsdomFileList = require('jsdom/lib/jsdom/living/generated/FileList');
    }
}

module.exports = EnvWithSyntheticFileList;

Para poder acceder a las importaciones 'externas'.

Esto me acercó, pero no puedo superar la validación interna:

exportaciones.is = valor => {
return utils.isObject (valor) && utils.hasOwn (valor, implSymbol) && value [implSymbol] instancia de Impl.implementation;
};

TypeError: No se pudo establecer la propiedad 'archivos' en 'HTMLInputElement': El valor proporcionado no es del tipo 'FileList'.

Pasó horas. Super frustrante

Esto parece haber cambiado en una versión reciente. Esto es lo que funcionó para mí:

const jsdomUtils = require('jsdom/lib/jsdom/living/generated/utils');
const jsdomFileList = require('jsdom/lib/jsdom/living/generated/FileList');
function makeFileList(...files) {
    const impl = jsdomFileList.createImpl(window);
    const ret = Object.assign([...files], {
        item: (ix) => ret[ix],
        [jsdomUtils.implSymbol]: impl,
    });
    impl[jsdomUtils.wrapperSymbol] = ret;
    Object.setPrototypeOf(ret, FileList.prototype);
    return ret;
}

Si está utilizando JSDOM a través de Jest, debe asegurarse de requerir los componentes internos fuera de la máquina virtual de prueba. Creé un entorno de prueba personalizado como este:

const JsdomEnvironment = require('jest-environment-jsdom');

/** See jsdom/jsdom#1272 */
class EnvWithSyntheticFileList extends JsdomEnvironment {
    async setup() {
        await super.setup();
        this.global.jsdomUtils = require('jsdom/lib/jsdom/living/generated/utils');
        this.global.jsdomFileList = require('jsdom/lib/jsdom/living/generated/FileList');
    }
}

module.exports = EnvWithSyntheticFileList;

Para poder acceder a las importaciones 'externas'.

Esto me acercó, pero no puedo superar la validación interna:

exportaciones.is = valor => {
return utils.isObject (valor) && utils.hasOwn (valor, implSymbol) && value [implSymbol] instancia de Impl.implementation;
};

TypeError: No se pudo establecer la propiedad 'archivos' en 'HTMLInputElement': El valor proporcionado no es del tipo 'FileList'.

Pasó horas. Super frustrante

Después de comenzar de nuevo con un cerebro nuevo, encontré el problema. De alguna manera, tenía 2 entornos jsdom separados en ejecución que eliminaron las referencias a los símbolos.

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

cg433n picture cg433n  ·  3Comentarios

khalyomede picture khalyomede  ·  3Comentarios

JacksonGariety picture JacksonGariety  ·  4Comentarios

josephrexme picture josephrexme  ·  4Comentarios

Progyan1997 picture Progyan1997  ·  3Comentarios